7cd02aeae199a75c7159196cbe6fb9c4594c8e60
[mono.git] / mcs / class / System.Drawing / System.Drawing / ColorConverter.cs
1 //
2 // System.Drawing.ColorConverter
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //      Ravindra (rkumar@novell.com)
7 //
8 // Copyright (C) 2002 Ximian, Inc.  http://www.ximian.com
9 // Copyright (C) 2004,2006,2008 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System.Collections;
32 using System.ComponentModel;
33 using System.Globalization;
34 using System.Text;
35 using System.ComponentModel.Design.Serialization;
36 using System.Reflection;
37
38 namespace System.Drawing
39 {
40         public class ColorConverter : TypeConverter
41         {
42                 static StandardValuesCollection cached;
43                 static object creatingCached = new object ();
44
45                 public ColorConverter () { }
46
47                 public override bool CanConvertFrom (ITypeDescriptorContext context, Type sourceType)
48                 {
49                         if (sourceType == typeof (string))
50                                 return true;
51
52                         return base.CanConvertFrom (context, sourceType);
53                 }
54
55                 public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
56                 {
57                         if (destinationType == typeof (InstanceDescriptor))
58                                 return true;
59
60                         return base.CanConvertTo (context, destinationType);
61                 }
62
63                 internal static Color StaticConvertFromString (ITypeDescriptorContext context, string s, CultureInfo culture)
64                 {
65                         if (culture == null)
66                                 culture = CultureInfo.InvariantCulture;
67
68                         s = s.Trim ();
69
70                         if (s.Length == 0)
71                                 return Color.Empty;
72
73                         // Try to process both NamedColor and SystemColors from the KnownColor enumeration
74                         if (Char.IsLetter (s [0])) {
75                                 KnownColor kc;
76                                 try {
77                                         kc = (KnownColor) Enum.Parse (typeof (KnownColor), s, true);
78                                 }
79                                 catch (Exception e) {
80                                         // whatever happens MS throws an basic Exception
81                                         string msg = Locale.GetText ("Invalid color name '{0}'.", s);
82                                         throw new Exception (msg, new FormatException (msg, e));
83                                 }
84                                 return KnownColors.FromKnownColor (kc);
85                         }
86
87                         String numSeparator = culture.TextInfo.ListSeparator;
88                         Color result = Color.Empty;
89
90                         if (s.IndexOf (numSeparator) == -1) {
91                                 bool sharp = (s[0] == '#');
92                                 int start = sharp ? 1 : 0;
93                                 bool hex = false;
94                                 // deal with #hex, 0xhex and #0xhex
95                                 if ((s.Length > start + 1) && (s[start] == '0')) {
96                                         hex = ((s[start + 1] == 'x') || (s[start + 1] == 'X'));
97                                         if (hex)
98                                                 start += 2;
99                                 }
100
101                                 if (sharp || hex) {
102                                         s = s.Substring (start);
103                                         int argb;
104                                         try {
105                                                 argb = Int32.Parse (s, NumberStyles.HexNumber);
106                                         }
107                                         catch (Exception e) {
108                                                 // whatever happens MS throws an basic Exception
109                                                 string msg = Locale.GetText ("Invalid Int32 value '{0}'.", s);
110                                                 throw new Exception (msg, e);
111                                         }
112
113                                         // note that the default alpha value for a 6 hex digit (i.e. when none are present) is 
114                                         // 0xFF while shorter string defaults to 0xFF - unless both # an 0x are specified
115                                         if ((s.Length < 6) || ((s.Length == 6) && sharp && hex))
116                                                 argb &= 0x00FFFFFF;
117                                         else if ((argb >> 24) == 0)
118                                                 argb |= unchecked((int)0xFF000000);
119                                         result = Color.FromArgb (argb);
120                                 }
121                         }
122
123                         if (result.IsEmpty) {
124                                 Int32Converter converter = new Int32Converter ();
125                                 String [] components = s.Split (numSeparator.ToCharArray ());
126
127                                 // MS seems to convert the indivual component to int before
128                                 // checking the number of components
129                                 int[] numComponents = new int[components.Length];
130                                 for (int i = 0; i < numComponents.Length; i++) {
131                                         numComponents[i] = (int) converter.ConvertFrom (context,
132                                                 culture, components[i]);
133                                 }
134
135                                 switch (components.Length) {
136                                         case 1:
137                                                 result = Color.FromArgb (numComponents[0]);
138                                                 break;
139                                         case 3:
140                                                 result = Color.FromArgb (numComponents[0], numComponents[1],
141                                                         numComponents[2]);
142                                                 break;
143                                         case 4:
144                                                 result = Color.FromArgb (numComponents[0], numComponents[1],
145                                                         numComponents[2], numComponents[3]);
146                                                 break;
147                                         default:
148                                                 throw new ArgumentException (s + " is not a valid color value.");
149                                 }
150                         } 
151
152                         if (!result.IsEmpty) {
153                                 // Look for a named or system color with those values
154                                 Color known = KnownColors.FindColorMatch (result);
155                                 if (!known.IsEmpty)
156                                         return known;
157                         }
158
159                         return result;
160                 }
161
162
163                 public override object ConvertFrom (ITypeDescriptorContext context,
164                                                     CultureInfo culture,
165                                                     object value)
166                 {
167                         string s = value as string;
168                         if (s == null)
169                                 return base.ConvertFrom (context, culture, value);
170
171                         return StaticConvertFromString (context, s, culture);
172                 }
173
174                 public override object ConvertTo (ITypeDescriptorContext context,
175                                                   CultureInfo culture,
176                                                   object value,
177                                                   Type destinationType)
178                 {
179                         if (value is Color) {
180                                 Color color = (Color) value;
181                                 if (destinationType == typeof (string)) {
182                                         if (color == Color.Empty)
183                                                 return string.Empty;
184
185                                         if (color.IsKnownColor || color.IsNamedColor)
186                                                 return color.Name;
187
188                                         String numSeparator = culture.TextInfo.ListSeparator;
189
190                                         StringBuilder sb = new StringBuilder ();
191                                         if (color.A != 255) {
192                                                 sb.Append (color.A);
193                                                 sb.Append (numSeparator);
194                                                 sb.Append (" ");
195                                         }
196                                         sb.Append (color.R);
197                                         sb.Append (numSeparator);
198                                         sb.Append (" ");
199
200                                         sb.Append (color.G);
201                                         sb.Append (numSeparator);
202                                         sb.Append (" ");
203
204                                         sb.Append (color.B);
205                                         return sb.ToString ();
206                                 } else if (destinationType == typeof (InstanceDescriptor)) {
207                                         if (color.IsEmpty) {
208                                                 return new InstanceDescriptor (typeof (Color).GetField ("Empty"), null);
209                                         } else if (color.IsSystemColor) {
210                                                 return new InstanceDescriptor (typeof (SystemColors).GetProperty (color.Name), null);
211                                         } else if (color.IsKnownColor){
212                                                 return new InstanceDescriptor (typeof (Color).GetProperty (color.Name), null);
213                                         } else {
214                                                 MethodInfo met = typeof(Color).GetMethod ("FromArgb", new Type[] { typeof(int), typeof(int), typeof(int), typeof(int) } );
215                                                 return new InstanceDescriptor (met, new object[] {color.A, color.R, color.G, color.B });
216                                         }
217                                 }
218                         }
219
220                         return base.ConvertTo (context, culture, value, destinationType);
221                 }
222
223                 public override StandardValuesCollection GetStandardValues (ITypeDescriptorContext context)
224                 {
225                         lock (creatingCached) {
226                                 if (cached != null)
227                                         return cached;
228 #if TARGET_JVM
229                                 Color [] colors = new Color [KnownColors.Values.Length - 1];
230                                 Array.Copy (KnownColors.Values, 1, colors, 0, colors.Length);
231 #else
232                                 Array colors = Array.CreateInstance (typeof (Color), KnownColors.ArgbValues.Length - 1);
233                                 for (int i=1; i < KnownColors.ArgbValues.Length; i++) {
234                                         colors.SetValue (KnownColors.FromKnownColor ((KnownColor)i), i - 1);
235                                 }
236 #endif
237
238                                 Array.Sort (colors, 0, colors.Length, new CompareColors ());
239                                 cached = new StandardValuesCollection (colors);
240                         }
241
242                         return cached;
243                 }
244
245                 public override bool GetStandardValuesSupported (ITypeDescriptorContext context)
246                 {
247                         return true;
248                 }
249
250                 sealed class CompareColors : IComparer {
251
252                         public int Compare (object x, object y)
253                         {
254                                 return String.Compare (((Color) x).Name, ((Color) y).Name);
255                         }
256                 }
257         }
258 }