///
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
///
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using JetBrains.Interop.WinApi;
using JetBrains.UI.Interop;
namespace JetBrains.UI.RichText
{
///
/// Represents a formatted string
///
public class RichString : IComparable, ICloneable
{
#region Constants
///
/// Wave length for weavy underlining
///
private const int WAVE_LENGTH = 6;
#endregion
private static FontCache ourFontCache = new FontCache();
#region Fields
///
/// String style
///
private TextStyle myStyle;
///
/// Starting offset of the corresponding string part
///
private int myOffset;
///
/// Length of the corresponding string part
///
private int myLength;
///
/// The text which the part is belonging to
///
private RichText myText;
#endregion
#region Properties
///
/// Gets or sets the string style
///
public TextStyle Style
{
get { return myStyle; }
set { myStyle = value; }
}
///
/// Gets or sets the starting offset
///
public int Offset
{
get { return myOffset; }
set { myOffset = value; }
}
///
/// Gets or sets the starting length
///
public int Length
{
get { return myLength; }
set { myLength = value; }
}
///
/// Gets the parent text
///
public RichText Text
{
get { return myText; }
}
#endregion
///
/// Creates a new instance.
///
/// String part offset
/// String part length
/// The style
/// The parent text block
/// text is null.
public RichString( int offset, int length, TextStyle style, RichText text )
{
myOffset = offset;
myLength = length;
myStyle = style;
myText = text;
}
public int GetSymbolByOffset( int x, RichTextParameters parameters, IntPtr hdc )
{
if (x < 0)
return -1;
Font hFont = GetParametrizedFont( parameters );
RECT rc = new RECT();
rc.left = 0;
rc.top = 0;
int currentX = 0;
IntPtr oldFont = Win32Declarations.SelectObject( hdc, ourFontCache.GetHFont( hFont ) );
try
{
for (int i = 0; i < PartText.Length; i++)
{
Win32Declarations.DrawText( hdc, PartText.Substring( i, 1 ), 1, ref rc, DrawTextFormatFlags.DT_CALCRECT | DrawTextFormatFlags.DT_SINGLELINE | DrawTextFormatFlags.DT_NOPREFIX | DrawTextFormatFlags.DT_NOCLIP );
currentX += rc.right - rc.left;
if (currentX > x)
return i;
}
return -1;
}
finally
{
Win32Declarations.SelectObject( hdc, oldFont );
}
}
///
/// Draws the formatted string on a given graphics
///
/// The device context to draw the string in.
/// Text formatting parameters
/// g is null
/// font is null
public int Draw( IntPtr hdc, Rectangle rect, RichTextParameters parameters )
{
Font hFont = GetParametrizedFont( parameters );
RECT rc = new RECT();
rc.left = rect.Left;
rc.top = rect.Top;
rc.bottom = rect.Bottom;
RectangleF bounds;
IntPtr oldFont = Win32Declarations.SelectObject( hdc, ourFontCache.GetHFont( hFont ) );
int oldColor = Win32Declarations.SetTextColor( hdc, Win32Declarations.ColorToRGB( myStyle.ForegroundColor ) );
int oldBkColor = Win32Declarations.SetBkColor( hdc, Win32Declarations.ColorToRGB( myStyle.BackgroundColor ) );
BackgroundMode oldBkMode = Win32Declarations.SetBkMode( hdc, myStyle.BackgroundColor == Color.Transparent ? BackgroundMode.TRANSPARENT : BackgroundMode.OPAQUE );
Win32Declarations.DrawText( hdc, PartText, PartText.Length, ref rc, DrawTextFormatFlags.DT_CALCRECT | DrawTextFormatFlags.DT_SINGLELINE | DrawTextFormatFlags.DT_NOPREFIX | DrawTextFormatFlags.DT_VCENTER | DrawTextFormatFlags.DT_NOCLIP );
if (rc.bottom > rect.Bottom) rc.bottom = rect.Bottom;
if (rc.right > rect.Right) rc.right = rect.Right;
bounds = new RectangleF( rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top );
Win32Declarations.DrawText( hdc, PartText, PartText.Length, ref rc, DrawTextFormatFlags.DT_SINGLELINE | DrawTextFormatFlags.DT_NOPREFIX | DrawTextFormatFlags.DT_VCENTER | DrawTextFormatFlags.DT_NOCLIP );
Win32Declarations.SetBkMode( hdc, oldBkMode );
Win32Declarations.SetBkColor( hdc, oldBkColor );
Win32Declarations.SetTextColor( hdc, oldColor );
Win32Declarations.SelectObject( hdc, oldFont );
switch (myStyle.Effect)
{
case TextStyle.EffectStyle.StrikeOut:
StrikeOut( hdc, bounds );
break;
case TextStyle.EffectStyle.StraightUnderline:
UnderlineStraight( hdc, bounds );
break;
case TextStyle.EffectStyle.WeavyUnderline:
UnderlineWeavy( hdc, bounds );
break;
}
return rc.right - rc.left;
}
///
/// Gets size of the string in the given graphics
///
/// The device context to calculate size in
/// Formatting parameters to use
/// Size of the string when drawn in a given graphics
/// g is null.
public SizeF GetSize( IntPtr hdc, RichTextParameters parameters )
{
Font hFont = GetParametrizedFont( parameters );
RECT rc = new RECT();
rc.left = 0;
rc.top = 0;
IntPtr oldFont = Win32Declarations.SelectObject( hdc, ourFontCache.GetHFont( hFont ) );
Win32Declarations.DrawText( hdc, PartText, PartText.Length, ref rc, DrawTextFormatFlags.DT_CALCRECT | DrawTextFormatFlags.DT_SINGLELINE | DrawTextFormatFlags.DT_NOPREFIX );
Win32Declarations.SelectObject( hdc, oldFont );
return new SizeF( rc.right - rc.left, rc.bottom - rc.top );
}
///
/// Gets text of the part
///
internal string PartText
{
get
{
if ( myOffset == 0 && myLength == myText.Text.Length )
{
return myText.Text;
}
return myText.Text.Substring( myOffset, myLength );
}
}
#region Private methods
///
/// Returns parametrized font
///
/// Formatting parameters to use
private Font GetParametrizedFont( RichTextParameters parameters )
{
return ourFontCache.GetFont( parameters.Font, myStyle.FontStyle );
}
///
/// Underlines text with text style color using straight line
///
private void UnderlineStraight( IntPtr hdc, RectangleF rect )
{
using (Graphics g = Graphics.FromHdc( hdc ))
g.DrawLine( new Pen( new SolidBrush( myStyle.EffectColor ) ), rect.Left, rect.Bottom, rect.Right, rect.Bottom );
}
///
/// Underlines text with text style color using weavy line
///
private void UnderlineWeavy( IntPtr hdc, RectangleF rect )
{
using (Graphics g = Graphics.FromHdc( hdc ))
{
Region clip = g.Clip;
g.SetClip( rect );
Pen pen = new Pen( new SolidBrush( myStyle.EffectColor ) );
for (float x = rect.Left; x <= rect.Right; x += WAVE_LENGTH)
{
g.DrawLine( pen, x, rect.Bottom, x + WAVE_LENGTH/2, rect.Bottom - 2 );
g.DrawLine( pen, x + WAVE_LENGTH/2, rect.Bottom, x + WAVE_LENGTH, rect.Bottom );
}
g.SetClip( clip, CombineMode.Replace );
}
}
///
/// Strikes out text with text style color using weavy line
///
private void StrikeOut( IntPtr hdc, RectangleF rect )
{
using (Graphics g = Graphics.FromHdc( hdc ))
g.DrawLine( new Pen( new SolidBrush( myStyle.EffectColor ) ), rect.Left, rect.Y + rect.Height/2, rect.Right, rect.Y + rect.Height/2 );
}
#endregion
#region ICloneable Members
public object Clone()
{
return new RichString( myOffset, myLength, myStyle, myText );
}
#endregion
#region IComparable Members
public int CompareTo( object obj )
{
if (!(obj is RichString))
return -1;
RichString s = (RichString) obj;
return s.Offset - myOffset;
}
#endregion
}
}