PageRenderTime 25ms CodeModel.GetById 16ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/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}