ImageList.cs: Use attribute constructors with less arguments where possible.
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ImageList.cs
1 //
2 // System.Windows.Forms.ImageList.cs
3 //
4 // Authors:
5 //   Peter Bartok <pbartok@novell.com>
6 //   Kornél Pál <http://www.kornelpal.hu/>
7 //
8 // Copyright (C) 2004-2005 Novell, Inc.
9 // Copyright (C) 2005 Kornél Pál
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 // COMPLETE
34
35 //
36 // Differences between MS.NET ImageList and this implementation:
37 //
38 // This is a fully managed image list implementation.
39 //
40 // Images are stored as Format32bppArgb internally but ColorDepth is applied
41 // to the colors of images. Images[index] returns a Format32bppArgb copy of
42 // the image so this difference is only internal.
43 //
44 // MS.NET has no alpha channel support (except for icons in 32-bit mode with
45 // comctl32.dll version 6.0) but this implementation has full alpha channel
46 // support in 32-bit mode.
47 //
48 // Handle should be an HIMAGELIST returned by ImageList_Create. This
49 // implementation uses (IntPtr)(-1) that is a non-zero but invalid handle.
50 //
51 // MS.NET destroys handles using the garbage collector this implementation
52 // does the same with Image objects stored in an ArrayList.
53 //
54 // MS.NET 1.x shares the same HIMAGELIST between ImageLists that were
55 // initialized from the same ImageListStreamer and doesn't update ImageSize
56 // and ColorDepth that are treated as bugs and MS.NET 2.0 behavior is
57 // implemented.
58 //
59 // MS.NET 2.0 initializes TransparentColor to Color.Transparent in
60 // constructors but ResetTransparentColor and ShouldSerializeTransparentColor
61 // default to Color.LightGray that is treated as a bug.
62 //
63 // MS.NET 2.0 does not clear keys when handle is destroyed that is treated as
64 // a bug.
65 //
66
67 using System.Collections;
68 using System.Collections.Specialized;
69 using System.ComponentModel;
70 using System.ComponentModel.Design.Serialization;
71 using System.Drawing;
72 using System.Drawing.Design;
73 using System.Drawing.Imaging;
74 using System.Globalization;
75 using System.Runtime.InteropServices;
76
77 namespace System.Windows.Forms
78 {
79         [DefaultProperty("Images")]
80         [Designer("System.Windows.Forms.Design.ImageListDesigner, " + Consts.AssemblySystem_Design)]
81 #if NET_2_0
82         [DesignerSerializer("System.Windows.Forms.Design.ImageListCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
83 #endif
84         [ToolboxItemFilter("System.Windows.Forms")]
85         [TypeConverter(typeof(ImageListConverter))]
86         public sealed class ImageList : System.ComponentModel.Component
87         {
88                 #region Private Fields
89                 private const ColorDepth DefaultColorDepth = ColorDepth.Depth8Bit;
90                 private static readonly Color DefaultTransparentColor = Color.Transparent;
91                 private static readonly Size DefaultImageSize = new Size(16, 16);
92
93 #if NET_2_0
94                 private object tag;
95 #endif
96                 private EventHandler recreateHandle;
97                 private readonly ImageCollection images;
98                 #endregion // Private Fields
99
100                 #region Sub-classes
101                 [Editor("System.Windows.Forms.Design.ImageCollectionEditor, " + Consts.AssemblySystem_Design, typeof(UITypeEditor))]
102                 public sealed class ImageCollection : IList, ICollection, IEnumerable
103                 {
104                         private const int AlphaMask = unchecked((int)0xFF000000);
105
106                         [StructLayout(LayoutKind.Explicit)]
107                         private struct ArgbColor
108                         {
109                                 [FieldOffset(0)]
110                                 internal int Argb;
111                                 [FieldOffset(0)]
112                                 internal byte Blue;
113                                 [FieldOffset(1)]
114                                 internal byte Green;
115                                 [FieldOffset(2)]
116                                 internal byte Red;
117                                 [FieldOffset(3)]
118                                 internal byte Alpha;
119                         }
120
121                         private
122 #if NET_2_0
123                         static
124 #else
125                         sealed
126 #endif
127                         class IndexedColorDepths
128                         {
129 #if !NET_2_0
130                                 private IndexedColorDepths()
131                                 {
132                                 }
133 #endif
134                                 internal static readonly ArgbColor[] Palette4Bit;
135                                 internal static readonly ArgbColor[] Palette8Bit;
136                                 private static readonly int[] squares;
137
138                                 static IndexedColorDepths()
139                                 {
140                                         Color[] palette;
141                                         Bitmap bitmap;
142                                         int index;
143                                         int count;
144
145                                         bitmap = new Bitmap(1, 1, PixelFormat.Format4bppIndexed);
146                                         palette = bitmap.Palette.Entries;
147                                         bitmap.Dispose();
148
149                                         Palette4Bit = new ArgbColor[count = palette.Length];
150                                         for (index = 0; index < count; index++)
151                                                 Palette4Bit[index].Argb = palette[index].ToArgb();
152
153                                         bitmap = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
154                                         palette = bitmap.Palette.Entries;
155                                         bitmap.Dispose();
156
157                                         Palette8Bit = new ArgbColor[count = palette.Length];
158                                         for (index = 0; index < count; index++)
159                                                 Palette8Bit[index].Argb = palette[index].ToArgb();
160
161                                         squares = new int[511];
162                                         for (index = 0; index < 256; index++)
163                                                 squares[255 + index] = squares[255 - index] = index * index;
164                                 }
165
166                                 internal static int GetNearestColor(ArgbColor[] palette, int color)
167                                 {
168                                         int index;
169                                         int count;
170                                         int red;
171                                         int green;
172                                         int blue;
173                                         int nearestColor;
174                                         int minDistance;
175                                         int distance;
176
177                                         count = palette.Length;
178                                         for (index = 0; index < count; index++)
179                                                 if (palette[index].Argb == color)
180                                                         return color;
181
182                                         red = unchecked((int)(unchecked((uint)color) >> 16) & 0xFF);
183                                         green = unchecked((int)(unchecked((uint)color) >> 8) & 0xFF);
184                                         blue = color & 0xFF;
185                                         nearestColor = AlphaMask;
186                                         minDistance = int.MaxValue;
187
188                                         for (index = 0; index < count; index++)
189                                                 if ((distance = squares[255 + palette[index].Red - red] + squares[255 + palette[index].Green - green] + squares[255 + palette[index].Blue - blue]) < minDistance) {
190                                                         nearestColor = palette[index].Argb;
191                                                         minDistance = distance;
192                                                 }
193
194                                         return nearestColor;
195                                 }
196                         }
197
198                         [Flags()]
199                         private enum ItemFlags
200                         {
201                                 None = 0,
202                                 UseTransparentColor = 1,
203                                 ImageStrip = 2
204                         }
205
206                         private sealed class ImageListItem
207                         {
208                                 internal readonly object Image;
209                                 internal readonly ItemFlags Flags;
210                                 internal readonly Color TransparentColor;
211                                 internal readonly int ImageCount = 1;
212
213                                 internal ImageListItem(Icon value)
214                                 {
215                                         if (value == null)
216                                                 throw new ArgumentNullException("value");
217
218                                         // Icons are cloned.
219                                         this.Image = (Icon)value.Clone();
220                                 }
221
222                                 internal ImageListItem(Image value)
223                                 {
224                                         if (value == null)
225                                                 throw new ArgumentNullException("value");
226
227                                         if (!(value is Bitmap))
228                                                 throw new ArgumentException("Image must be a Bitmap.");
229
230                                         // Images are not cloned.
231                                         this.Image = value;
232                                 }
233
234                                 internal ImageListItem(Image value, Color transparentColor) : this(value)
235                                 {
236                                         this.Flags = ItemFlags.UseTransparentColor;
237                                         this.TransparentColor = transparentColor;
238                                 }
239
240                                 internal ImageListItem(Image value, int imageCount) : this(value)
241                                 {
242                                         this.Flags = ItemFlags.ImageStrip;
243                                         this.ImageCount = imageCount;
244                                 }
245                         }
246
247                         #region ImageCollection Private Fields
248                         private ColorDepth colorDepth = DefaultColorDepth;
249                         private Color transparentColor = DefaultTransparentColor;
250                         private Size imageSize = DefaultImageSize;
251                         private ArrayList list = new ArrayList();
252 #if NET_2_0
253                         private ArrayList keys = new ArrayList();
254 #endif
255                         private int count;
256                         private bool handleCreated;
257 #if NET_2_0
258                         private int lastKeyIndex = -1;
259 #endif
260                         private readonly ImageList owner;
261                         #endregion // ImageCollection Private Fields
262
263                         #region ImageCollection Internal Constructors
264                         // For use in ImageList
265                         internal ImageCollection(ImageList owner)
266                         {
267                                 this.owner = owner;
268                         }
269                         #endregion // ImageCollection Internal Constructor
270
271                         #region ImageCollection Internal Instance Properties
272                         // For use in ImageList
273                         internal ColorDepth ColorDepth {
274                                 get {
275                                         return this.colorDepth;
276                                 }
277
278                                 set {
279                                         if (!Enum.IsDefined(typeof(ColorDepth), value))
280                                                 throw new InvalidEnumArgumentException("value", (int)value, typeof(ColorDepth));
281
282                                         if (this.colorDepth != value) {
283                                                 this.colorDepth = value;
284                                                 RecreateHandle();
285                                         }
286                                 }
287                         }
288
289                         // For use in ImageList
290                         internal IntPtr Handle {
291                                 get {
292                                         CreateHandle();
293                                         return (IntPtr)(-1);
294                                 }
295                         }
296
297                         // For use in ImageList
298                         internal bool HandleCreated {
299                                 get {
300                                         return this.handleCreated;
301                                 }
302                         }
303
304                         // For use in ImageList
305                         internal Size ImageSize {
306                                 get {
307                                         return this.imageSize;
308                                 }
309
310                                 set {
311                                         if (value.Width < 1 || value.Width > 256 || value.Height < 1 || value.Height > 256)
312                                                 throw new ArgumentException("ImageSize.Width and Height must be between 1 and 256", "value");
313
314                                         if (this.imageSize != value) {
315                                                 this.imageSize = value;
316                                                 RecreateHandle();
317                                         }
318                                 }
319                         }
320
321                         // For use in ImageList
322                         internal ImageListStreamer ImageStream {
323                                 get {
324                                         return this.Empty ? null : new ImageListStreamer(this);
325                                 }
326
327                                 set {
328                                         int index;
329                                         Image[] streamImages;
330
331                                         if (value == null) {
332 #if NET_2_0
333                                                 if (this.handleCreated)
334                                                         DestroyHandle();
335                                                 else
336                                                         this.Clear();
337 #endif
338                                         }
339                                         // Only deserialized ImageListStreamers are used.
340                                         else if ((streamImages = value.Images) != null) {
341                                                 this.list = new ArrayList(streamImages.Length);
342                                                 this.count = 0;
343                                                 this.handleCreated = true;
344 #if NET_2_0
345                                                 this.keys = new ArrayList(streamImages.Length);
346 #endif
347
348                                                 for (index = 0; index < streamImages.Length; index++) {
349                                                         list.Add((Image)streamImages[index].Clone());
350 #if NET_2_0
351                                                         keys.Add(null);
352 #endif
353                                                 }
354
355                                                 this.imageSize = value.ImageSize;
356                                                 this.colorDepth = value.ColorDepth;
357
358 #if NET_2_0
359                                                 // Event is raised even when handle was not created yet.
360                                                 owner.OnRecreateHandle();
361 #endif
362                                         }
363                                 }
364                         }
365
366                         // For use in ImageList
367                         internal Color TransparentColor {
368                                 get {
369                                         return this.transparentColor;
370                                 }
371
372                                 set {
373                                         this.transparentColor = value;
374                                 }
375                         }
376                         #endregion // ImageCollection Internal Instance Properties
377
378                         #region ImageCollection Public Instance Properties
379                         [Browsable(false)]
380                         public int Count {
381                                 get {
382                                         return this.handleCreated ? list.Count : this.count;
383                                 }
384                         }
385
386                         public bool Empty {
387                                 get {
388                                         return this.Count == 0;
389                                 }
390                         }
391
392                         public bool IsReadOnly {
393                                 get {
394                                         return false;
395                                 }
396                         }
397
398                         [Browsable(false)]
399                         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
400                         public Image this[int index] {
401                                 get {
402                                         return (Image)GetImage(index).Clone();
403                                 }
404
405                                 set {
406                                         Image image;
407
408                                         if (index < 0 || index >= this.Count)
409                                                 throw new ArgumentOutOfRangeException("index");
410
411                                         if (value == null)
412                                                 throw new ArgumentNullException("value");
413
414                                         if (!(value is Bitmap))
415                                                 throw new ArgumentException("Image must be a Bitmap.");
416
417                                         image = CreateImage(value, this.transparentColor);
418                                         CreateHandle();
419                                         list[index] = image;
420                                 }
421                         }
422
423 #if NET_2_0
424                         public Image this[string key] {
425                                 get {
426                                         int index;
427
428                                         return (index = IndexOfKey(key)) == -1 ? null : this[index];
429                                 }
430                         }
431
432                         public StringCollection Keys {
433                                 get {
434                                         int index;
435                                         string key;
436                                         StringCollection keyCollection;
437
438                                         // Returns all keys even when there are more keys than
439                                         // images. Null keys are returned as empty strings.
440
441                                         keyCollection = new StringCollection();
442                                         for (index = 0; index < keys.Count; index++)
443                                                 keyCollection.Add(((key = (string)keys[index]) == null || key.Length == 0) ? string.Empty : key);
444
445                                         return keyCollection;
446                                 }
447                         }
448 #endif
449                         #endregion // ImageCollection Public Instance Properties
450
451                         #region ImageCollection Private Static Methods
452 #if NET_2_0
453                         private static bool CompareKeys(string key1, string key2)
454                         {
455                                 // Keys are case-insensitive and keys with different length
456                                 // are not equal even when string.Compare treats them equal.
457
458                                 if (key1 == null || key2 == null || key1.Length != key2.Length)
459                                         return false;
460
461                                 return string.Compare(key1, key2, true, CultureInfo.InvariantCulture) == 0;
462                         }
463 #endif
464                         #endregion // ImageCollection Private Static Methods
465
466                         #region ImageCollection Private Instance Methods
467 #if NET_2_0
468                         private int AddItem(string key, ImageListItem item)
469 #else
470                         private int AddItem(ImageListItem item)
471 #endif
472                         {
473                                 int itemIndex;
474 #if NET_2_0
475                                 int index;
476 #endif
477
478                                 if (this.handleCreated)
479                                         itemIndex = AddItemInternal(item);
480                                 else {
481                                         // Image strips are counted as a single item in the return
482                                         // value of Add and AddStrip until handle is created.
483
484                                         itemIndex = list.Add(item);
485                                         this.count += item.ImageCount;
486                                 }
487
488 #if NET_2_0
489                                 if ((item.Flags & ItemFlags.ImageStrip) == 0)
490                                         keys.Add(key);
491                                 else
492                                         for (index = 0; index < item.ImageCount; index++)
493                                                 keys.Add(null);
494 #endif
495
496                                 return itemIndex;
497                         }
498
499                         private int AddItemInternal(ImageListItem item)
500                         {
501                                 if (item.Image is Icon) {
502                                         int imageWidth;
503                                         int imageHeight;
504                                         Bitmap bitmap;
505                                         Graphics graphics;
506
507                                         bitmap = new Bitmap(imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
508                                         graphics = Graphics.FromImage(bitmap);
509                                         graphics.DrawIcon((Icon)item.Image, new Rectangle(0, 0, imageWidth, imageHeight));
510                                         graphics.Dispose();
511
512                                         ReduceColorDepth(bitmap);
513                                         return list.Add(bitmap);
514                                 }
515                                 else if ((item.Flags & ItemFlags.ImageStrip) == 0)
516                                         return list.Add(CreateImage((Image)item.Image, (item.Flags & ItemFlags.UseTransparentColor) == 0 ? this.transparentColor : item.TransparentColor));
517                                 else {
518                                         int imageX;
519                                         int width;
520                                         int imageWidth;
521                                         int imageHeight;
522                                         int index;
523                                         Image image;
524                                         Bitmap bitmap;
525                                         Graphics graphics;
526                                         Rectangle imageRect;
527                                         ImageAttributes imageAttributes;
528
529                                         // When ImageSize was changed after adding image strips
530                                         // Count will return invalid values based on old ImageSize
531                                         // but when creating handle either ArgumentException will
532                                         // be thrown or image strip will be added according to the
533                                         // new ImageSize. This can result in image count
534                                         // difference that can result in exceptions in methods
535                                         // that use Count before creating handle. In addition this
536                                         // can result in the loss of sync with keys. When doing
537                                         // the same after handle was created there are no problems
538                                         // as handle will be recreated after changing ImageSize
539                                         // that results in the loss of images added previously.
540
541                                         if ((width = (image = (Image)item.Image).Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
542                                                 throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
543
544                                         if (image.Height != (imageHeight = this.imageSize.Height))
545                                                 throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
546
547                                         imageRect = new Rectangle(0, 0, imageWidth, imageHeight);
548                                         if (this.transparentColor.A == 0)
549                                                 imageAttributes = null;
550                                         else {
551                                                 imageAttributes = new ImageAttributes();
552                                                 imageAttributes.SetColorKey(this.transparentColor, this.transparentColor);
553                                         }
554
555                                         index = list.Count;
556                                         for (imageX = 0; imageX < width; imageX += imageWidth) {
557                                                 bitmap = new Bitmap(imageWidth, imageHeight, PixelFormat.Format32bppArgb);
558                                                 graphics = Graphics.FromImage(bitmap);
559                                                 graphics.DrawImage(image, imageRect, imageX, 0, imageWidth, imageHeight, GraphicsUnit.Pixel, imageAttributes);
560                                                 graphics.Dispose();
561
562                                                 ReduceColorDepth(bitmap);
563                                                 list.Add(bitmap);
564                                         }
565                                         return index;
566                                 }
567                         }
568
569                         private void CreateHandle()
570                         {
571                                 int index;
572                                 ArrayList items;
573
574                                 if (!this.handleCreated) {
575                                         items = this.list;
576                                         this.list = new ArrayList(this.count);
577                                         this.count = 0;
578                                         this.handleCreated = true;
579
580                                         for (index = 0; index < items.Count; index++)
581                                                 AddItemInternal((ImageListItem)items[index]);
582                                 }
583                         }
584
585                         private Image CreateImage(Image value, Color transparentColor)
586                         {
587                                 int imageWidth;
588                                 int imageHeight;
589                                 Bitmap bitmap;
590                                 Graphics graphics;
591                                 ImageAttributes imageAttributes;
592
593                                 if (transparentColor.A == 0)
594                                         imageAttributes = null;
595                                 else {
596                                         imageAttributes = new ImageAttributes();
597                                         imageAttributes.SetColorKey(transparentColor, transparentColor);
598                                 }
599
600                                 bitmap = new Bitmap(imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
601                                 graphics = Graphics.FromImage(bitmap);
602                                 graphics.DrawImage(value, new Rectangle(0, 0, imageWidth, imageHeight), 0, 0, value.Width, value.Height, GraphicsUnit.Pixel, imageAttributes);
603                                 graphics.Dispose();
604
605                                 ReduceColorDepth(bitmap);
606                                 return bitmap;
607                         }
608
609                         private void RecreateHandle()
610                         {
611                                 if (this.handleCreated) {
612                                         DestroyHandle();
613                                         this.handleCreated = true;
614                                         owner.OnRecreateHandle();
615                                 }
616                         }
617
618                         private unsafe void ReduceColorDepth(Bitmap bitmap)
619                         {
620                                 byte* pixelPtr;
621                                 byte* lineEndPtr;
622                                 byte* linePtr;
623                                 int line;
624                                 int pixel;
625                                 int height;
626                                 int widthBytes;
627                                 int stride;
628                                 BitmapData bitmapData;
629                                 ArgbColor[] palette;
630
631                                 if (this.colorDepth < ColorDepth.Depth32Bit) {
632                                         bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
633                                         try {
634                                                 linePtr = (byte*)bitmapData.Scan0;
635                                                 height = bitmapData.Height;
636                                                 widthBytes = bitmapData.Width << 2;
637                                                 stride = bitmapData.Stride;
638
639                                                 if (this.colorDepth < ColorDepth.Depth16Bit) {
640                                                         palette = this.colorDepth < ColorDepth.Depth8Bit ? IndexedColorDepths.Palette4Bit : IndexedColorDepths.Palette8Bit;
641
642                                                         for (line = 0; line < height; line++) {
643                                                                 lineEndPtr = linePtr + widthBytes;
644                                                                 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
645                                                                         *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : IndexedColorDepths.GetNearestColor(palette, pixel | AlphaMask);
646                                                                 linePtr += stride;
647                                                         }
648                                                 }
649                                                 else if (this.colorDepth < ColorDepth.Depth24Bit) {
650                                                         for (line = 0; line < height; line++) {
651                                                                 lineEndPtr = linePtr + widthBytes;
652                                                                 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
653                                                                         *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : (pixel & 0x00F8F8F8) | AlphaMask;
654                                                                 linePtr += stride;
655                                                         }
656                                                 }
657                                                 else {
658                                                         for (line = 0; line < height; line++) {
659                                                                 lineEndPtr = linePtr + widthBytes;
660                                                                 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
661                                                                         *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : pixel | AlphaMask;
662                                                                 linePtr += stride;
663                                                         }
664                                                 }
665                                         }
666                                         finally {
667                                                 bitmap.UnlockBits(bitmapData);
668                                         }
669                                 }
670                         }
671                         #endregion // ImageCollection Private Instance Methods
672
673                         #region ImageCollection Internal Instance Methods
674                         // For use in ImageList
675                         internal void DestroyHandle()
676                         {
677                                 if (this.handleCreated) {
678                                         this.list = new ArrayList();
679                                         this.count = 0;
680                                         this.handleCreated = false;
681 #if NET_2_0
682                                         keys = new ArrayList();
683 #endif
684                                 }
685                         }
686
687                         // For use in ImageList
688                         internal Image GetImage(int index)
689                         {
690                                 if (index < 0 || index >= this.Count)
691                                         throw new ArgumentOutOfRangeException("index");
692
693                                 CreateHandle();
694                                 return (Image)list[index];
695                         }
696
697                         // For use in ImageListStreamer
698                         internal Image[] ToArray()
699                         {
700                                 Image[] images;
701
702                                 // Handle is created even when the list is empty.
703                                 CreateHandle();
704                                 images = new Image[list.Count];
705                                 list.CopyTo(images);
706                                 return images;
707                         }
708                         #endregion // ImageCollection Internal Instance Methods
709
710                         #region ImageCollection Public Instance Methods
711                         public void Add(Icon value)
712                         {
713 #if NET_2_0
714                                 Add(null, value);
715 #else
716                                 AddItem(new ImageListItem(value));
717 #endif
718                         }
719
720                         public void Add(Image value)
721                         {
722 #if NET_2_0
723                                 Add(null, value);
724 #else
725                                 AddItem(new ImageListItem(value));
726 #endif
727                         }
728
729                         public int Add(Image value, Color transparentColor)
730                         {
731 #if NET_2_0
732                                 return AddItem(null, new ImageListItem(value, transparentColor));
733 #else
734                                 return AddItem(new ImageListItem(value, transparentColor));
735 #endif
736                         }
737
738 #if NET_2_0
739                         public void Add(string key, Icon icon)
740                         {
741                                 // Argument has name icon but exceptions use name value.
742                                 AddItem(key, new ImageListItem(icon));
743                         }
744
745                         public void Add(string key, Image image)
746                         {
747                                 // Argument has name image but exceptions use name value.
748                                 AddItem(key, new ImageListItem(image));
749                         }
750
751                         public void AddRange(Image[] images)
752                         {
753                                 int index;
754
755                                 if (images == null)
756                                         throw new ArgumentNullException("images");
757
758                                 for (index = 0; index < images.Length; index++)
759                                         Add(images[index]);
760                         }
761 #endif
762
763                         public int AddStrip(Image value)
764                         {
765                                 int width;
766                                 int imageWidth;
767
768                                 if (value == null)
769                                         throw new ArgumentNullException("value");
770
771                                 if ((width = value.Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
772                                         throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
773
774                                 if (value.Height != this.imageSize.Height)
775                                         throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
776
777 #if NET_2_0
778                                 return AddItem(null, new ImageListItem(value, width / imageWidth));
779 #else
780                                 return AddItem(new ImageListItem(value, width / imageWidth));
781 #endif
782                         }
783
784                         public void Clear()
785                         {
786                                 list.Clear();
787                                 if (this.handleCreated)
788                                         this.count = 0;
789 #if NET_2_0
790                                 keys.Clear();
791 #endif
792                         }
793
794 #if NET_2_0
795                         [EditorBrowsable(EditorBrowsableState.Never)]
796 #endif
797                         public bool Contains(Image image)
798                         {
799                                 throw new NotSupportedException();
800                         }
801
802 #if NET_2_0
803                         public bool ContainsKey(string key)
804                         {
805                                 return IndexOfKey(key) != -1;
806                         }
807 #endif
808
809                         public IEnumerator GetEnumerator()
810                         {
811                                 Image[] images = new Image[this.Count];
812                                 int index;
813
814                                 if (images.Length != 0) {
815                                         // Handle is created only when there are images.
816                                         CreateHandle();
817
818                                         for (index = 0; index < images.Length; index++)
819                                                 images[index] = (Image)((Image)list[index]).Clone();
820                                 }
821
822                                 return images.GetEnumerator();
823                         }
824
825 #if NET_2_0
826                         [EditorBrowsable(EditorBrowsableState.Never)]
827 #endif
828                         public int IndexOf(Image image)
829                         {
830                                 throw new NotSupportedException();
831                         }
832
833 #if NET_2_0
834                         public int IndexOfKey(string key)
835                         {
836                                 int index;
837
838                                 if (key != null && key.Length != 0) {
839                                         // When last IndexOfKey was successful and the same key was
840                                         // assigned to an image with a lower index than the last
841                                         // result and the key of the last result equals to key
842                                         // argument the last result is returned.
843
844                                         if (this.lastKeyIndex >= 0 && this.lastKeyIndex < this.Count && CompareKeys((string)keys[this.lastKeyIndex], key))
845                                                 return this.lastKeyIndex;
846
847                                         // Duplicate keys are allowed and first match is returned.
848                                         for (index = 0; index < this.Count; index++)
849                                                 if (CompareKeys((string)keys[index], key))
850                                                         return this.lastKeyIndex = index;
851                                 }
852
853                                 return this.lastKeyIndex = -1;
854                         }
855 #endif
856
857 #if NET_2_0
858                         [EditorBrowsable(EditorBrowsableState.Never)]
859 #endif
860                         public void Remove(Image image)
861                         {
862                                 throw new NotSupportedException();
863                         }
864
865                         public void RemoveAt(int index)
866                         {
867                                 if (index < 0 || index >= this.Count)
868                                         throw new ArgumentOutOfRangeException("index");
869
870                                 CreateHandle();
871                                 list.RemoveAt(index);
872 #if NET_2_0
873                                 keys.RemoveAt(index);
874 #endif
875                         }
876
877 #if NET_2_0
878                         public void RemoveByKey(string key)
879                         {
880                                 int index;
881
882                                 if ((index = IndexOfKey(key)) != -1)
883                                         RemoveAt(index);
884                         }
885
886                         public void SetKeyName(int index, string name)
887                         {
888                                 // Only SetKeyName throws IndexOutOfRangeException.
889                                 if (index < 0 || index >= this.Count)
890                                         throw new IndexOutOfRangeException();
891
892                                 keys[index] = name;
893                         }
894 #endif
895                         #endregion // ImageCollection Public Instance Methods
896
897                         #region ImageCollection Interface Properties
898                         object IList.this[int index] {
899                                 get {
900                                         return this[index];
901                                 }
902
903                                 set {
904                                         if (!(value is Image))
905                                                 throw new ArgumentException("value");
906
907                                         this[index] = (Image)value;
908                                 }
909                         }
910
911                         bool IList.IsFixedSize {
912                                 get {
913                                         return false;
914                                 }
915                         }
916
917                         bool ICollection.IsSynchronized {
918                                 get {
919                                         return false;
920                                 }
921                         }
922
923                         object ICollection.SyncRoot {
924                                 get {
925                                         return this;
926                                 }
927                         }
928                         #endregion // ImageCollection Interface Properties
929
930                         #region ImageCollection Interface Methods
931                         int IList.Add(object value)
932                         {
933                                 int index;
934
935                                 if (!(value is Image))
936                                         throw new ArgumentException("value");
937
938                                 index = this.Count;
939                                 this.Add((Image)value);
940                                 return index;
941                         }
942
943                         bool IList.Contains(object value)
944                         {
945                                 return value is Image ? this.Contains((Image)value) : false;
946                         }
947
948                         int IList.IndexOf(object value)
949                         {
950                                 return value is Image ? this.IndexOf((Image)value) : -1;
951                         }
952
953                         void IList.Insert(int index, object value)
954                         {
955                                 throw new NotSupportedException();
956                         }
957
958                         void IList.Remove(object value)
959                         {
960                                 if (value is Image)
961                                         this.Remove((Image)value);
962                         }
963
964                         void ICollection.CopyTo(Array array, int index)
965                         {
966                                 int imageIndex;
967
968                                 for (imageIndex = 0; imageIndex < this.Count; imageIndex++)
969                                         array.SetValue(this[index], index++);
970                         }
971                         #endregion // ImageCollection Interface Methods
972                 }
973                 #endregion // Sub-classes
974
975                 #region Public Constructors
976                 public ImageList()
977                 {
978                         images = new ImageCollection(this);
979                 }
980
981                 public ImageList(System.ComponentModel.IContainer container) : this()
982                 {
983                         container.Add(this);
984                 }
985                 #endregion // Public Constructors
986
987                 #region Private Instance Methods
988                 private void OnRecreateHandle()
989                 {
990                         if (recreateHandle != null)
991                                 recreateHandle(this, EventArgs.Empty);
992                 }
993
994 #if NET_2_0
995                 // For use in Designers
996                 private void ResetColorDepth()
997                 {
998                         this.ColorDepth = DefaultColorDepth;
999                 }
1000
1001                 // For use in Designers
1002                 private void ResetImageSize()
1003                 {
1004                         this.ImageSize = DefaultImageSize;
1005                 }
1006
1007                 // For use in Designers
1008                 private void ResetTransparentColor()
1009                 {
1010                         this.TransparentColor = DefaultTransparentColor;
1011                 }
1012
1013                 // For use in Designers
1014                 private bool ShouldSerializeColorDepth()
1015                 {
1016                         // ColorDepth is serialized in ImageStream when non-empty.
1017                         // It is serialized even if it has its default value when empty.
1018                         return images.Empty;
1019                 }
1020
1021                 // For use in Designers
1022                 private bool ShouldSerializeImageSize()
1023                 {
1024                         // ImageSize is serialized in ImageStream when non-empty.
1025                         // It is serialized even if it has its default value when empty.
1026                         return images.Empty;
1027                 }
1028
1029                 // For use in Designers
1030                 private bool ShouldSerializeTransparentColor()
1031                 {
1032                         return this.TransparentColor != DefaultTransparentColor;
1033                 }
1034 #endif
1035                 #endregion // Private Instance Methods
1036
1037                 #region Public Instance Properties
1038 #if !NET_2_0
1039                 [DefaultValue(DefaultColorDepth)]
1040 #endif
1041                 public ColorDepth ColorDepth {
1042                         get {
1043                                 return images.ColorDepth;
1044                         }
1045
1046                         set {
1047                                 images.ColorDepth = value;
1048                         }
1049                 }
1050
1051                 [Browsable(false)]
1052                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1053                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1054                 public IntPtr Handle {
1055                         get {
1056                                 return images.Handle;
1057                         }
1058                 }
1059
1060                 [Browsable(false)]
1061                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1062                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1063                 public bool HandleCreated {
1064                         get {
1065                                 return images.HandleCreated;
1066                         }
1067                 }
1068
1069                 [DefaultValue(null)]
1070                 [MergableProperty(false)]
1071                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1072                 public ImageCollection Images {
1073                         get {
1074                                 return this.images;
1075                         }
1076                 }
1077
1078                 [Localizable(true)]
1079                 public Size ImageSize {
1080                         get {
1081                                 return images.ImageSize;
1082                         }
1083
1084                         set {
1085                                 images.ImageSize = value;
1086                         }
1087                 }
1088
1089                 [Browsable(false)]
1090                 [DefaultValue(null)]
1091                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1092                 public ImageListStreamer ImageStream {
1093                         get {
1094                                 return images.ImageStream;
1095                         }
1096
1097                         set {
1098                                 images.ImageStream = value;
1099                         }
1100                 }
1101
1102 #if NET_2_0
1103                 [Bindable(true)]
1104                 [DefaultValue(null)]
1105                 [Localizable(false)]
1106                 [TypeConverter(typeof(StringConverter))]
1107                 public object Tag {
1108                         get {
1109                                 return this.tag;
1110                         }
1111
1112                         set {
1113                                 this.tag = value;
1114                         }
1115                 }
1116 #endif
1117
1118                 public Color TransparentColor {
1119                         get {
1120                                 return images.TransparentColor;
1121                         }
1122
1123                         set {
1124                                 images.TransparentColor = value;
1125                         }
1126                 }
1127                 #endregion // Public Instance Properties
1128
1129                 #region Public Instance Methods
1130                 public void Draw(Graphics g, Point pt, int index)
1131                 {
1132                         this.Draw(g, pt.X, pt.Y, index);
1133                 }
1134
1135                 public void Draw(Graphics g, int x, int y, int index)
1136                 {
1137                         g.DrawImage(images.GetImage(index), x, y);
1138                 }
1139
1140                 public void Draw(Graphics g, int x, int y, int width, int height, int index)
1141                 {
1142                         g.DrawImage(images.GetImage(index), x, y, width, height);
1143                 }
1144
1145                 public override string ToString()
1146                 {
1147                         return base.ToString() + " Images.Count: " + images.Count.ToString() + ", ImageSize: " + images.ImageSize.ToString();
1148                 }
1149                 #endregion // Public Instance Methods
1150
1151                 #region Protected Instance Methods
1152                 protected override void Dispose(bool disposing)
1153                 {
1154                         if (disposing)
1155                                 images.DestroyHandle();
1156
1157                         base.Dispose(disposing);
1158                 }
1159                 #endregion // Protected Instance Methods
1160
1161                 #region Events
1162                 [Browsable(false)]
1163                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1164                 public event EventHandler RecreateHandle {
1165                         add {
1166                                 recreateHandle += value;
1167                         }
1168
1169                         remove {
1170                                 recreateHandle -= value;
1171                         }
1172                 }
1173                 #endregion // Events
1174         }
1175 }