|
//------------------------------------------------------------------------------
// <copyright file="ColorConverter.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
*/
namespace System.Drawing {
using System.Runtime.Serialization.Formatters;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Win32;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Globalization;
using System.Reflection;
using System.Threading;
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter"]/*' />
/// <devdoc>
/// ColorConverter is a class that can be used to convert
/// colors from one data type to another. Access this
/// class through the TypeDescriptor.
/// </devdoc>
public class ColorConverter : TypeConverter {
private static string ColorConstantsLock = "colorConstants";
private static Hashtable colorConstants;
private static string SystemColorConstantsLock = "systemColorConstants";
private static Hashtable systemColorConstants;
private static string ValuesLock = "values";
private static StandardValuesCollection values;
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.ColorConverter"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public ColorConverter() {
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.Colors"]/*' />
/// <devdoc>
/// Hashtable of color / value pairs (color name is key)
/// for standard colors.
/// </devdoc>
private static Hashtable Colors {
get {
if (colorConstants == null) {
lock(ColorConstantsLock) {
if (colorConstants == null) {
Hashtable tempHash = new Hashtable(StringComparer.OrdinalIgnoreCase);
FillConstants(tempHash, typeof(Color));
colorConstants = tempHash;
}
}
}
return colorConstants;
}
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.SystemColors"]/*' />
/// <devdoc>
/// Hashtable of color / value pairs (color name is key)
/// for system colors.
/// </devdoc>
private static Hashtable SystemColors {
get {
if (systemColorConstants == null) {
lock (SystemColorConstantsLock) {
if (systemColorConstants == null) {
Hashtable tempHash = new Hashtable(StringComparer.OrdinalIgnoreCase);
FillConstants(tempHash, typeof(System.Drawing.SystemColors));
systemColorConstants = tempHash;
}
}
}
return systemColorConstants;
}
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.CanConvertFrom"]/*' />
/// <devdoc>
/// Determines if this converter can convert an object in the given source
/// type to the native type of the converter.
/// </devdoc>
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
if (sourceType == typeof(string)) {
return true;
}
return base.CanConvertFrom(context, sourceType);
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.CanConvertTo"]/*' />
/// <devdoc>
/// <para>Gets a value indicating whether this converter can
/// convert an object to the given destination type using the context.</para>
/// </devdoc>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
if (destinationType == typeof(InstanceDescriptor)) {
return true;
}
return base.CanConvertTo(context, destinationType);
}
internal static object GetNamedColor(string name) {
object color = null;
// First, check to see if this is a standard name.
//
color = Colors[name];
if (color != null) {
return color;
}
// Ok, how about a system color?
//
color = SystemColors[name];
return color;
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.ConvertFrom"]/*' />
/// <devdoc>
/// Converts the given object to the converter's native type.
/// </devdoc>
[SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")]
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
string strValue = value as string;
if (strValue != null) {
object obj = null;
string text = strValue.Trim();
if (text.Length == 0) {
obj = Color.Empty;
}
else {
// First, check to see if this is a standard name.
//
obj = GetNamedColor(text);
if (obj == null) {
if (culture == null) {
culture = CultureInfo.CurrentCulture;
}
char sep = culture.TextInfo.ListSeparator[0];
bool tryMappingToKnownColor = true;
TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int));
// If the value is a 6 digit hex number only, then
// we want to treat the Alpha as 255, not 0
//
if (text.IndexOf(sep) == -1) {
// text can be '' (empty quoted string)
if (text.Length >= 2 && (text[0] == '\'' || text[0] == '"') && text[0] == text[text.Length -1]) {
// In quotes means a named value
string colorName = text.Substring(1, text.Length - 2);
obj = Color.FromName(colorName);
tryMappingToKnownColor = false;
}
else if ((text.Length == 7 && text[0] == '#') ||
(text.Length == 8 && (text.StartsWith("0x") || text.StartsWith("0X"))) ||
(text.Length == 8 && (text.StartsWith("&h") || text.StartsWith("&H")))) {
// Note: ConvertFromString will raise exception if value cannot be converted.
obj = Color.FromArgb(unchecked((int)(0xFF000000 | (uint)(int)intConverter.ConvertFromString(context, culture, text))));
}
}
// Nope. Parse the RGBA from the text.
//
if (obj == null) {
string[] tokens = text.Split(new char[] {sep});
int[] values = new int[tokens.Length];
for (int i = 0; i < values.Length; i++) {
values[i] = unchecked((int)intConverter.ConvertFromString(context, culture, tokens[i]));
}
// We should now have a number of parsed integer values.
// We support 1, 3, or 4 arguments:
//
// 1 -- full ARGB encoded
// 3 -- RGB
// 4 -- ARGB
//
switch (values.Length) {
case 1:
obj = Color.FromArgb(values[0]);
break;
case 3:
obj = Color.FromArgb(values[0], values[1], values[2]);
break;
case 4:
obj = Color.FromArgb(values[0], values[1], values[2], values[3]);
break;
}
tryMappingToKnownColor = true;
}
if ((obj != null) && tryMappingToKnownColor) {
// Now check to see if this color matches one of our known colors.
// If it does, then substitute it. We can only do this for "Colors"
// because system colors morph with user settings.
//
int targetARGB = ((Color)obj).ToArgb();
foreach (Color c in Colors.Values) {
if (c.ToArgb() == targetARGB) {
obj = c;
break;
}
}
}
}
if (obj == null) {
throw new ArgumentException(SR.GetString(SR.InvalidColor, text));
}
}
return obj;
}
return base.ConvertFrom(context, culture, value);
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.ConvertTo"]/*' />
/// <devdoc>
/// Converts the given object to another type. The most common types to convert
/// are to and from a string object. The default implementation will make a call
/// to ToString on the object if the object is valid and if the destination
/// type is string. If this cannot convert to the desitnation type, this will
/// throw a NotSupportedException.
/// </devdoc>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
if (destinationType == null) {
throw new ArgumentNullException("destinationType");
}
if( value is Color ){
if (destinationType == typeof(string)) {
Color c = (Color)value;
if (c == Color.Empty) {
return string.Empty;
}
else {
// If this is a known color, then Color can provide its own
// name. Otherwise, we fabricate an ARGB value for it.
//
if (c.IsKnownColor) {
return c.Name;
}
else if (c.IsNamedColor) {
return "'" + c.Name + "'";
}
else {
if (culture == null) {
culture = CultureInfo.CurrentCulture;
}
string sep = culture.TextInfo.ListSeparator + " ";
TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int));
string[] args;
int nArg = 0;
if (c.A < 255) {
args = new string[4];
args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.A);
}
else {
args = new string[3];
}
// Note: ConvertToString will raise exception if value cannot be converted.
args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.R);
args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.G);
args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.B);
// Now slam all of these together with the fantastic Join
// method.
//
return string.Join(sep, args);
}
}
}
if (destinationType == typeof(InstanceDescriptor)) {
MemberInfo member = null;
object[] args = null;
Color c = (Color)value;
if (c.IsEmpty) {
member = typeof(Color).GetField("Empty");
}
else if (c.IsSystemColor) {
member = typeof(SystemColors).GetProperty(c.Name);
}
else if (c.IsKnownColor) {
member = typeof(Color).GetProperty(c.Name);
}
else if (c.A != 255) {
member = typeof(Color).GetMethod("FromArgb", new Type[] {typeof(int), typeof(int), typeof(int), typeof(int)});
args = new object[] {c.A, c.R, c.G, c.B};
}
else if (c.IsNamedColor) {
member = typeof(Color).GetMethod("FromName", new Type[] {typeof(string)});
args = new object[] {c.Name};
}
else {
member = typeof(Color).GetMethod("FromArgb", new Type[] {typeof(int), typeof(int), typeof(int)});
args = new object[] {c.R, c.G, c.B};
}
Debug.Assert(member != null, "Could not convert color to member. Did someone change method name / signature and not update Colorconverter?");
if (member != null) {
return new InstanceDescriptor(member, args);
}
else {
return null;
}
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.FillConstants"]/*' />
/// <devdoc>
/// Fills the given hashtable with field name / value pairs. It walks all public static
/// properties of enumType that have a property type of Color.
/// </devdoc>
private static void FillConstants(Hashtable hash, Type enumType) {
MethodAttributes attrs = MethodAttributes.Public | MethodAttributes.Static;
PropertyInfo[] props = enumType.GetProperties();
for (int i = 0; i < props.Length; i++) {
PropertyInfo prop = props[i];
if (prop.PropertyType == typeof(Color)) {
MethodInfo method = prop.GetGetMethod();
if (method != null && (method.Attributes & attrs) == attrs) {
object[] tempIndex = null;
hash[prop.Name] = prop.GetValue(null, tempIndex);
}
}
}
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.GetStandardValues"]/*' />
/// <devdoc>
/// Retrieves a collection containing a set of standard values
/// for the data type this validator is designed for. This
/// will return null if the data type does not support a
/// standard set of values.
/// </devdoc>
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) {
if (values == null) {
lock (ValuesLock) {
if (values == null) {
// We must take the value from each hashtable and combine them.
//
ArrayList arrayValues = new ArrayList();
arrayValues.AddRange(Colors.Values);
arrayValues.AddRange(SystemColors.Values);
// Now, we have a couple of colors that have the same names but
// are identical values. Look for these and remove them. Too
// bad this is n^2.
//
int count = arrayValues.Count;
for (int i = 0; i < count - 1; i++) {
for (int j = i + 1; j < count; j++) {
if (arrayValues[i].Equals(arrayValues[j])) {
// Remove this item!
//
arrayValues.RemoveAt(j);
count--;
j--;
}
}
}
// Sort the array.
//
arrayValues.Sort(0, arrayValues.Count, new ColorComparer());
values = new StandardValuesCollection(arrayValues.ToArray());
}
}
}
return values;
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.GetStandardValuesSupported"]/*' />
/// <devdoc>
/// Determines if this object supports a standard set of values
/// that can be picked from a list.
/// </devdoc>
public override bool GetStandardValuesSupported(ITypeDescriptorContext context) {
return true;
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.ColorComparer"]/*' />
/// <devdoc>
/// IComparer for color values. This takes color values but compares their
/// names.
/// </devdoc>
private class ColorComparer : IComparer {
public int Compare(object left, object right) {
Color cLeft = (Color)left;
Color cRight = (Color)right;
return string.Compare(cLeft.Name, cRight.Name, false, CultureInfo.InvariantCulture);
}
}
}
}
|