You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
7.3 KiB
193 lines
7.3 KiB
// <file>
|
|
// <copyright see="prj:///doc/copyright.txt"/>
|
|
// <license see="prj:///doc/license.txt"/>
|
|
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
|
|
// <version>$Revision$</version>
|
|
// </file>
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using ICSharpCode.TextEditor.Document;
|
|
|
|
namespace ICSharpCode.TextEditor
|
|
{
|
|
/// <summary>
|
|
/// A class that is able to draw a line on any control (outside the text editor)
|
|
/// </summary>
|
|
public class DrawableLine
|
|
{
|
|
private static readonly StringFormat sf = (StringFormat)StringFormat.GenericTypographic.Clone();
|
|
private readonly Font boldMonospacedFont;
|
|
private readonly Font monospacedFont;
|
|
|
|
private readonly List<SimpleTextWord> words = new List<SimpleTextWord>();
|
|
private SizeF spaceSize;
|
|
|
|
public DrawableLine(IDocument document, LineSegment line, Font monospacedFont, Font boldMonospacedFont)
|
|
{
|
|
this.monospacedFont = monospacedFont;
|
|
this.boldMonospacedFont = boldMonospacedFont;
|
|
if (line.Words != null)
|
|
foreach (var 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), Bold: false, SystemColors.WindowText));
|
|
}
|
|
|
|
public int LineLength
|
|
{
|
|
get
|
|
{
|
|
var length = 0;
|
|
foreach (var 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;
|
|
var pos = 0;
|
|
for (var i = 0; i < words.Count; i++)
|
|
{
|
|
var word = words[i];
|
|
if (pos >= endIndex)
|
|
break;
|
|
var 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
|
|
var inRegionLength = endIndex - pos;
|
|
var 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(startIndex: 0, inRegionLength);
|
|
}
|
|
else if (startIndex < wordEnd)
|
|
{
|
|
// end of word is in region (or middle of word is in region)
|
|
var notInRegionLength = startIndex - pos;
|
|
|
|
var 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(startIndex: 0, notInRegionLength);
|
|
}
|
|
|
|
pos = wordEnd;
|
|
}
|
|
}
|
|
|
|
public static float DrawDocumentWord(Graphics g, string word, PointF position, Font font, Color foreColor)
|
|
{
|
|
if (string.IsNullOrEmpty(word))
|
|
return 0f;
|
|
var wordSize = g.MeasureString(word, font, width: 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(x: 0, y: 0), sf);
|
|
return spaceSize;
|
|
}
|
|
|
|
public void DrawLine(Graphics g, ref float xPos, float xOffset, float yPos, Color c)
|
|
{
|
|
var spaceSize = GetSpaceSize(g);
|
|
foreach (var word in words)
|
|
switch (word.Type)
|
|
{
|
|
case TextWordType.Space:
|
|
xPos += spaceSize.Width;
|
|
break;
|
|
case TextWordType.Tab:
|
|
var 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)
|
|
{
|
|
var spaceSize = GetSpaceSize(g);
|
|
foreach (var word in words)
|
|
switch (word.Type)
|
|
{
|
|
case TextWordType.Space:
|
|
xPos += spaceSize.Width;
|
|
break;
|
|
case TextWordType.Tab:
|
|
var tabWidth = spaceSize.Width*4;
|
|
xPos += tabWidth;
|
|
xPos = (int)((xPos + 2)/tabWidth)*tabWidth;
|
|
break;
|
|
case TextWordType.Word:
|
|
if (!string.IsNullOrEmpty(word.Word))
|
|
xPos += g.MeasureString(word.Word, word.Bold ? boldMonospacedFont : monospacedFont, width: 32768, sf).Width;
|
|
break;
|
|
}
|
|
return xPos;
|
|
}
|
|
|
|
private class SimpleTextWord
|
|
{
|
|
internal static readonly SimpleTextWord Space = new SimpleTextWord(TextWordType.Space, " ", Bold: false, SystemColors.WindowText);
|
|
internal static readonly SimpleTextWord Tab = new SimpleTextWord(TextWordType.Tab, "\t", Bold: false, SystemColors.WindowText);
|
|
internal readonly Color Color;
|
|
internal readonly TextWordType Type;
|
|
internal bool Bold;
|
|
internal string Word;
|
|
|
|
public SimpleTextWord(TextWordType Type, string Word, bool Bold, Color Color)
|
|
{
|
|
this.Type = Type;
|
|
this.Word = Word;
|
|
this.Bold = Bold;
|
|
this.Color = Color;
|
|
}
|
|
}
|
|
}
|
|
}
|