/src/NUnit/UiException/Controls/DefaultCodeRenderer.cs
C# | 199 lines | 130 code | 42 blank | 27 comment | 9 complexity | 19d468164fcd44b1912ac2670dcae233 MD5 | raw file
1// **************************************************************** 2// This is free software licensed under the NUnit license. You may 3// obtain a copy of the license at http://nunit.org 4// **************************************************************** 5 6using System; 7using System.Collections.Generic; 8using System.Text; 9using NUnit.UiException.CodeFormatters; 10using System.Drawing; 11using System.Diagnostics; 12 13namespace NUnit.UiException.Controls 14{ 15 public class DefaultCodeRenderer : 16 ICodeRenderer 17 { 18 /// <summary> 19 /// These constants below address an issue at measure text time 20 /// that sometimes can cause big lines of text to be misaligned. 21 /// </summary> 22 private readonly static float MEASURECHAR_BIG_WIDTH = 5000f; 23 private readonly static float MEASURECHAR_BIG_HEIGHT = 100f; 24 25 public PaintLineLocation[] ViewportLines(FormattedCode code, RectangleF viewport, float fontHeight) 26 { 27 List<PaintLineLocation> list = new List<PaintLineLocation>(); 28 int visibles = CountVisibleLines(viewport.Height, fontHeight); 29// int topIndex = YCoordinateToLineIndex(viewport.Top, fontHeight); 30 int lineIndex = YCoordinateToLineIndex(viewport.Top, fontHeight); 31 int i; 32 33 for (i = 0; i < visibles; ++i, lineIndex++) 34 { 35 if (lineIndex < 0) 36 continue; 37 38 if (lineIndex >= code.LineCount) 39 break; 40 41 list.Add( 42 new PaintLineLocation(lineIndex, code.GetTextAt(lineIndex), 43 new PointF(-viewport.Left, 44 LineIndexToYCoordinate(lineIndex, fontHeight) - 45 viewport.Top))); 46 } 47 48 return (list.ToArray()); 49 } 50 51 #region ICodeRenderer Membres 52 53 public void DrawToGraphics(FormattedCode code, CodeRenderingContext args, Rectangle viewport) 54 { 55 UiExceptionHelper.CheckNotNull(code, "code"); 56 UiExceptionHelper.CheckNotNull(args, "args"); 57 58 ClassifiedTokenCollection line; 59 PaintLineLocation[] lines; 60 ClassifiedToken token; 61 float fontHeight; 62 string text; 63 float tk_width; 64 int i; 65 float x; 66 67 fontHeight = LineIndexToYCoordinate(1, args.Graphics, args.Font); 68 lines = ViewportLines(code, viewport, fontHeight); 69 70 foreach (PaintLineLocation paintLine in lines) 71 { 72 // All lines that differ from CurrentLine are displayed 73 // in using different styles of Brush to make a distinction 74 // between code, keyword, comments. 75 if (paintLine.LineIndex != args.CurrentLine) 76 { 77 line = code[paintLine.LineIndex]; 78 x = 0; 79 text = line.Text; 80 81 for (i = 0; i < line.Count; ++i) 82 { 83 token = line[i]; 84 85 args.Graphics.DrawString(token.Text, args.Font, args.GetBrush(token.Tag), 86 paintLine.Location.X + x, paintLine.Location.Y); 87 88 tk_width = measureStringWidth(args.Graphics, args.Font, text, 89 token.IndexStart, token.Text.Length); 90 91 x += tk_width; 92 } 93 94 continue; 95 } 96 97 // The current line is emphasized by using a 98 // specific couples of Background & Foreground colors 99 100 args.Graphics.FillRectangle( 101 args.CurrentLineBackBrush, 102 0, paintLine.Location.Y, 103 viewport.Width, fontHeight); 104 105 args.Graphics.DrawString( 106 paintLine.Text, args.Font, 107 args.CurrentLineForeBrush, 108 paintLine.Location.X, paintLine.Location.Y); 109 } 110 111 return; 112 } 113 114 public SizeF GetDocumentSize(FormattedCode code, Graphics g, Font font) 115 { 116 UiExceptionHelper.CheckNotNull(code, "code"); 117 UiExceptionHelper.CheckNotNull(g, "g"); 118 UiExceptionHelper.CheckNotNull(font, "font"); 119 120 StringBuilder sample; 121 SizeF measure; 122 int i; 123 124 sample = new StringBuilder(); 125 for (i = code.MaxLength; i > 0; --i) 126 sample.Append("m"); 127 128 measure = g.MeasureString(sample.ToString(), font); 129 130 return (new SizeF(measure.Width, measure.Height * code.LineCount)); 131 } 132 133 public float LineIndexToYCoordinate(int lineIndex, Graphics g, Font font) 134 { 135 UiExceptionHelper.CheckNotNull(g, "g"); 136 UiExceptionHelper.CheckNotNull(font, "font"); 137 138 SizeF sz = g.MeasureString("m", font); 139 return (lineIndex * sz.Height); 140 } 141 142 #endregion 143 144 /// <summary> 145 /// Utility method that measures a region of text in the given string. 146 /// </summary> 147 /// <param name="g">The graphics instance used to render this text.</param> 148 /// <param name="font">The font instance used to render this text.</param> 149 /// <param name="text">The text that contains the region to be rendered.</param> 150 /// <param name="indexStart">Starting startingPosition of this region.</param> 151 /// <param name="length">Length of this region.</param> 152 /// <returns>The width of this region of text.</returns> 153 private float measureStringWidth(Graphics g, Font font, string text, int indexStart, int length) 154 { 155 CharacterRange[] ranges; 156 StringFormat sf; 157 Region[] regions; 158 159 if (length == 0) 160 return (0); 161 162 length = Math.Min(length, text.Length); 163 164 ranges = new CharacterRange[] { new CharacterRange(indexStart, length) }; 165 sf = new StringFormat(); 166 167 // the string of text may contains white spaces that need to 168 // be measured correctly. 169 170 sf.FormatFlags = StringFormatFlags.MeasureTrailingSpaces; 171 172 sf.SetMeasurableCharacterRanges(ranges); 173 174 // here : giving a layout too small can cause returned measure 175 // to be wrong. 176 177 regions = g.MeasureCharacterRanges( 178 text, font, new RectangleF( 179 0, 0, MEASURECHAR_BIG_WIDTH, MEASURECHAR_BIG_HEIGHT), sf); 180 181 return (regions[0].GetBounds(g).Width); 182 } 183 184 int CountVisibleLines(float viewportHeight, float fontHeight) 185 { 186 return ((int)(viewportHeight / fontHeight) + 1); 187 } 188 189 int YCoordinateToLineIndex(float y, float fontHeight) 190 { 191 return (int)(y / fontHeight); 192 } 193 194 float LineIndexToYCoordinate(int index, float fontHeight) 195 { 196 return (index * fontHeight); 197 } 198 } 199}