// // // // // $Revision$ // using System; using System.Collections.Generic; using System.Drawing; using ICSharpCode.TextEditor.Document; namespace ICSharpCode.TextEditor { /// /// A class that is able to draw a line on any control (outside the text editor) /// public class DrawableLine { static StringFormat sf = (StringFormat)System.Drawing.StringFormat.GenericTypographic.Clone(); List words = new List(); SizeF spaceSize; Font monospacedFont; Font boldMonospacedFont; private class SimpleTextWord { internal TextWordType Type; internal string Word; internal bool Bold; internal Color Color; public SimpleTextWord(TextWordType Type, string Word, bool Bold, Color Color) { this.Type = Type; this.Word = Word; this.Bold = Bold; this.Color = Color; } internal readonly static SimpleTextWord Space = new SimpleTextWord(TextWordType.Space, " ", false, Color.Black); internal readonly static SimpleTextWord Tab = new SimpleTextWord(TextWordType.Tab, "\t", false, Color.Black); } public DrawableLine(IDocument document, LineSegment line, Font monospacedFont, Font boldMonospacedFont) { this.monospacedFont = monospacedFont; this.boldMonospacedFont = boldMonospacedFont; if (line.Words != null) { foreach (TextWord word in line.Words) { if (word.Type == TextWordType.Space) { words.Add(SimpleTextWord.Space); } else if (word.Type == TextWordType.Tab) { words.Add(SimpleTextWord.Tab); } else { words.Add(new SimpleTextWord(TextWordType.Word, word.Word, word.Bold, word.Color)); } } } else { words.Add(new SimpleTextWord(TextWordType.Word, document.GetText(line), false, Color.Black)); } } public int LineLength { get { int length = 0; foreach (SimpleTextWord word in words) { length += word.Word.Length; } return length; } } public void SetBold(int startIndex, int endIndex, bool bold) { if (startIndex < 0) throw new ArgumentException("startIndex must be >= 0"); if (startIndex > endIndex) throw new ArgumentException("startIndex must be <= endIndex"); if (startIndex == endIndex) return; int pos = 0; for (int i = 0; i < words.Count; i++) { SimpleTextWord word = words[i]; if (pos >= endIndex) break; int wordEnd = pos + word.Word.Length; // 3 possibilities: if (startIndex <= pos && endIndex >= wordEnd) { // word is fully in region: word.Bold = bold; } else if (startIndex <= pos) { // beginning of word is in region int inRegionLength = endIndex - pos; SimpleTextWord newWord = new SimpleTextWord(word.Type, word.Word.Substring(inRegionLength), word.Bold, word.Color); words.Insert(i + 1, newWord); word.Bold = bold; word.Word = word.Word.Substring(0, inRegionLength); } else if (startIndex < wordEnd) { // end of word is in region (or middle of word is in region) int notInRegionLength = startIndex - pos; SimpleTextWord newWord = new SimpleTextWord(word.Type, word.Word.Substring(notInRegionLength), word.Bold, word.Color); // newWord.Bold will be set in the next iteration words.Insert(i + 1, newWord); word.Word = word.Word.Substring(0, notInRegionLength); } pos = wordEnd; } } public static float DrawDocumentWord(Graphics g, string word, PointF position, Font font, Color foreColor) { if (word == null || word.Length == 0) { return 0f; } SizeF wordSize = g.MeasureString(word, font, 32768, sf); g.DrawString(word, font, BrushRegistry.GetBrush(foreColor), position, sf); return wordSize.Width; } public SizeF GetSpaceSize(Graphics g) { if (spaceSize.IsEmpty) { spaceSize = g.MeasureString("-", boldMonospacedFont, new PointF(0, 0), sf); } return spaceSize; } public void DrawLine(Graphics g, ref float xPos, float xOffset, float yPos, Color c) { SizeF spaceSize = GetSpaceSize(g); foreach (SimpleTextWord word in words) { switch (word.Type) { case TextWordType.Space: xPos += spaceSize.Width; break; case TextWordType.Tab: float tabWidth = spaceSize.Width * 4; xPos += tabWidth; xPos = (int)((xPos + 2) / tabWidth) * tabWidth; break; case TextWordType.Word: xPos += DrawDocumentWord(g, word.Word, new PointF(xPos + xOffset, yPos), word.Bold ? boldMonospacedFont : monospacedFont, c == Color.Empty ? word.Color : c ); break; } } } public void DrawLine(Graphics g, ref float xPos, float xOffset, float yPos) { DrawLine(g, ref xPos, xOffset, yPos, Color.Empty); } public float MeasureWidth(Graphics g, float xPos) { SizeF spaceSize = GetSpaceSize(g); foreach (SimpleTextWord word in words) { switch (word.Type) { case TextWordType.Space: xPos += spaceSize.Width; break; case TextWordType.Tab: float tabWidth = spaceSize.Width * 4; xPos += tabWidth; xPos = (int)((xPos + 2) / tabWidth) * tabWidth; break; case TextWordType.Word: if (word.Word != null && word.Word.Length > 0) { xPos += g.MeasureString(word.Word, word.Bold ? boldMonospacedFont : monospacedFont, 32768, sf).Width; } break; } } return xPos; } } }