#region Using Directives using System; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.Text; using System.Reflection; #endregion Using Directives namespace ScintillaNET { /// /// Determines how whitespace should be displayed in a control. /// /// /// By default, whitespace is determined by the lexer in use. Setting the /// or properties overrides the lexer behavior. /// [TypeConverterAttribute(typeof(WhitespaceConverter))] public class Whitespace : TopLevelHelper { #region Constants private const string BACK_COLORBAG = "Whitespace.BackColor"; private const string FORE_COLORBAG = "Whitespace.ForeColor"; private const string ALPHA_EXCEPTION = "Transparent colors are not supported."; // TODO Move into resource file. #endregion Constants #region Properties /// /// Gets or sets the whitespace background color. /// /// /// By default, the whitespace background color is determined by the lexer in use. /// Setting the BackColor to anything other than overrides the lexer behavior. /// Transparent colors are not supported. /// /// /// A that represents the background color of whitespace characters. /// The default is . /// /// /// The specified has an alpha value that is less that . /// [DefaultValue(typeof(Color), ""), NotifyParentProperty(true), RefreshProperties(RefreshProperties.Repaint)] [Category("Appearance"), Description("The background color of whitespace characters.")] public Color BackColor { get { Color value; Scintilla.ColorBag.TryGetValue(BACK_COLORBAG, out value); return value; } set { if (value != Color.Empty && value.A < Byte.MaxValue) throw new ArgumentException(ALPHA_EXCEPTION); if (value != BackColor) { if (value == Color.Empty) { Scintilla.ColorBag.Remove(BACK_COLORBAG); NativeScintilla.SetWhitespaceBack(false, 0); } else { Scintilla.ColorBag[BACK_COLORBAG] = value; NativeScintilla.SetWhitespaceBack(true, Utilities.ColorToRgb(value)); } } } } /// /// Gets or sets the whitespace foreground color. /// /// /// By default, the whitespace foreground color is determined by the lexer in use. /// Setting the ForeColor to anything other than overrides the lexer behavior. /// Transparent colors are not supported. /// /// /// A that represents the foreground color of whitespace characters. /// The default is . /// /// /// The specified has an alpha value that is less that . /// [DefaultValue(typeof(Color), ""), NotifyParentProperty(true), RefreshProperties(RefreshProperties.Repaint)] [Category("Appearance"), Description("The foreground color of whitespace characters.")] public Color ForeColor { get { Color value; Scintilla.ColorBag.TryGetValue(FORE_COLORBAG, out value); return value; } set { if (value != Color.Empty && value.A < Byte.MaxValue) throw new ArgumentException(ALPHA_EXCEPTION); if (value != ForeColor) { if (value == Color.Empty) { Scintilla.ColorBag.Remove(FORE_COLORBAG); NativeScintilla.SetWhitespaceFore(false, 0xFFFFFF); } else { Scintilla.ColorBag[FORE_COLORBAG] = value; NativeScintilla.SetWhitespaceFore(true, Utilities.ColorToRgb(value)); } } } } /// /// Gets or sets the whitespace display mode. /// /// One of the values. The default is /// /// The specified value is not a valid value. /// [DefaultValue(WhitespaceMode.Invisible), NotifyParentProperty(true), RefreshProperties(RefreshProperties.Repaint)] [Category("Appearance"), Description("The mode used to display whitespace.")] public WhitespaceMode Mode { get { return (WhitespaceMode)NativeScintilla.GetViewWs(); } set { if (!Enum.IsDefined(typeof(WhitespaceMode), value)) throw new InvalidEnumArgumentException("value", (int)value, typeof(WhitespaceMode)); if (value != Mode) NativeScintilla.SetViewWs((int)value); } } #endregion Properties #region Constructors internal Whitespace(Scintilla scintilla) : base(scintilla) { } #endregion Constructors #region Types // This is an experimental type converter that will automatically build a string representation // of all the public properties on the object. It provides better user feedback than the standard ExpandableObjectConverter. // It is currently private to the Whitespace class because that is the only place it has been tested // but it has obvious applications outside of this one class. private class WhitespaceConverter : ExpandableObjectConverter { public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { // We only care about string representations... if (destinationType != typeof(String)) base.ConvertTo(context, culture, value, destinationType); try { // Enumerate all the public properties StringBuilder sb = new StringBuilder(); foreach (PropertyInfo pi in context.PropertyDescriptor.PropertyType.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { // Look for a DefaultAttribute and see if the value has changed object[] attributes = pi.GetCustomAttributes(typeof(DefaultValueAttribute), true); if (attributes != null && attributes.Length > 0) { DefaultValueAttribute dv = (DefaultValueAttribute)attributes[0]; object propertyValue = pi.GetValue(value, null); if (!dv.Value.Equals(propertyValue)) { // Create a string representation of the value if (sb.Length > 0) sb.Append("; "); sb.Append(pi.Name); sb.Append("="); TypeConverter tc = TypeDescriptor.GetConverter(pi.PropertyType); if (tc != null) sb.Append(tc.ConvertToString(propertyValue)); else if (propertyValue != null) sb.Append(propertyValue.ToString()); } } else { // TODO Look for a "ShouldSerialize..." method to determine if the value has changed } } return sb.ToString(); } catch { // Don't throw an exception, just don't display anything return String.Empty; } } } #endregion Types } }