start populating the new System.Web.Configuration_2.0 dir
[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
566                                         if (imageAttributes != null)
567                                                 imageAttributes.Dispose();
568
569                                         return index;
570                                 }
571                         }
572
573                         private void CreateHandle()
574                         {
575                                 int index;
576                                 ArrayList items;
577
578                                 if (!this.handleCreated) {
579                                         items = this.list;
580                                         this.list = new ArrayList(this.count);
581                                         this.count = 0;
582                                         this.handleCreated = true;
583
584                                         for (index = 0; index < items.Count; index++)
585                                                 AddItemInternal((ImageListItem)items[index]);
586                                 }
587                         }
588
589                         private Image CreateImage(Image value, Color transparentColor)
590                         {
591                                 int imageWidth;
592                                 int imageHeight;
593                                 Bitmap bitmap;
594                                 Graphics graphics;
595                                 ImageAttributes imageAttributes;
596
597                                 if (transparentColor.A == 0)
598                                         imageAttributes = null;
599                                 else {
600                                         imageAttributes = new ImageAttributes();
601                                         imageAttributes.SetColorKey(transparentColor, transparentColor);
602                                 }
603
604                                 bitmap = new Bitmap(imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
605                                 graphics = Graphics.FromImage(bitmap);
606                                 graphics.DrawImage(value, new Rectangle(0, 0, imageWidth, imageHeight), 0, 0, value.Width, value.Height, GraphicsUnit.Pixel, imageAttributes);
607                                 graphics.Dispose();
608
609                                 if (imageAttributes != null)
610                                         imageAttributes.Dispose();
611
612                                 ReduceColorDepth(bitmap);
613                                 return bitmap;
614                         }
615
616                         private void RecreateHandle()
617                         {
618                                 if (this.handleCreated) {
619                                         DestroyHandle();
620                                         this.handleCreated = true;
621                                         owner.OnRecreateHandle();
622                                 }
623                         }
624
625                         private unsafe void ReduceColorDepth(Bitmap bitmap)
626                         {
627                                 byte* pixelPtr;
628                                 byte* lineEndPtr;
629                                 byte* linePtr;
630                                 int line;
631                                 int pixel;
632                                 int height;
633                                 int widthBytes;
634                                 int stride;
635                                 BitmapData bitmapData;
636                                 ArgbColor[] palette;
637
638                                 if (this.colorDepth < ColorDepth.Depth32Bit) {
639                                         bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
640                                         try {
641                                                 linePtr = (byte*)bitmapData.Scan0;
642                                                 height = bitmapData.Height;
643                                                 widthBytes = bitmapData.Width << 2;
644                                                 stride = bitmapData.Stride;
645
646                                                 if (this.colorDepth < ColorDepth.Depth16Bit) {
647                                                         palette = this.colorDepth < ColorDepth.Depth8Bit ? IndexedColorDepths.Palette4Bit : IndexedColorDepths.Palette8Bit;
648
649                                                         for (line = 0; line < height; line++) {
650                                                                 lineEndPtr = linePtr + widthBytes;
651                                                                 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
652                                                                         *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : IndexedColorDepths.GetNearestColor(palette, pixel | AlphaMask);
653                                                                 linePtr += stride;
654                                                         }
655                                                 }
656                                                 else if (this.colorDepth < ColorDepth.Depth24Bit) {
657                                                         for (line = 0; line < height; line++) {
658                                                                 lineEndPtr = linePtr + widthBytes;
659                                                                 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
660                                                                         *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : (pixel & 0x00F8F8F8) | AlphaMask;
661                                                                 linePtr += stride;
662                                                         }
663                                                 }
664                                                 else {
665                                                         for (line = 0; line < height; line++) {
666                                                                 lineEndPtr = linePtr + widthBytes;
667                                                                 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
668                                                                         *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : pixel | AlphaMask;
669                                                                 linePtr += stride;
670                                                         }
671                                                 }
672                                         }
673                                         finally {
674                                                 bitmap.UnlockBits(bitmapData);
675                                         }
676                                 }
677                         }
678                         #endregion // ImageCollection Private Instance Methods
679
680                         #region ImageCollection Internal Instance Methods
681                         // For use in ImageList
682                         internal void DestroyHandle()
683                         {
684                                 if (this.handleCreated) {
685                                         this.list = new ArrayList();
686                                         this.count = 0;
687                                         this.handleCreated = false;
688 #if NET_2_0
689                                         keys = new ArrayList();
690 #endif
691                                 }
692                         }
693
694                         // For use in ImageList
695                         internal Image GetImage(int index)
696                         {
697                                 if (index < 0 || index >= this.Count)
698                                         throw new ArgumentOutOfRangeException("index");
699
700                                 CreateHandle();
701                                 return (Image)list[index];
702                         }
703
704                         // For use in ImageListStreamer
705                         internal Image[] ToArray()
706                         {
707                                 Image[] images;
708
709                                 // Handle is created even when the list is empty.
710                                 CreateHandle();
711                                 images = new Image[list.Count];
712                                 list.CopyTo(images);
713                                 return images;
714                         }
715                         #endregion // ImageCollection Internal Instance Methods
716
717                         #region ImageCollection Public Instance Methods
718                         public void Add(Icon value)
719                         {
720 #if NET_2_0
721                                 Add(null, value);
722 #else
723                                 AddItem(new ImageListItem(value));
724 #endif
725                         }
726
727                         public void Add(Image value)
728                         {
729 #if NET_2_0
730                                 Add(null, value);
731 #else
732                                 AddItem(new ImageListItem(value));
733 #endif
734                         }
735
736                         public int Add(Image value, Color transparentColor)
737                         {
738 #if NET_2_0
739                                 return AddItem(null, new ImageListItem(value, transparentColor));
740 #else
741                                 return AddItem(new ImageListItem(value, transparentColor));
742 #endif
743                         }
744
745 #if NET_2_0
746                         public void Add(string key, Icon icon)
747                         {
748                                 // Argument has name icon but exceptions use name value.
749                                 AddItem(key, new ImageListItem(icon));
750                         }
751
752                         public void Add(string key, Image image)
753                         {
754                                 // Argument has name image but exceptions use name value.
755                                 AddItem(key, new ImageListItem(image));
756                         }
757
758                         public void AddRange(Image[] images)
759                         {
760                                 int index;
761
762                                 if (images == null)
763                                         throw new ArgumentNullException("images");
764
765                                 for (index = 0; index < images.Length; index++)
766                                         Add(images[index]);
767                         }
768 #endif
769
770                         public int AddStrip(Image value)
771                         {
772                                 int width;
773                                 int imageWidth;
774
775                                 if (value == null)
776                                         throw new ArgumentNullException("value");
777
778                                 if ((width = value.Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
779                                         throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
780
781                                 if (value.Height != this.imageSize.Height)
782                                         throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
783
784 #if NET_2_0
785                                 return AddItem(null, new ImageListItem(value, width / imageWidth));
786 #else
787                                 return AddItem(new ImageListItem(value, width / imageWidth));
788 #endif
789                         }
790
791                         public void Clear()
792                         {
793                                 list.Clear();
794                                 if (this.handleCreated)
795                                         this.count = 0;
796 #if NET_2_0
797                                 keys.Clear();
798 #endif
799                         }
800
801 #if NET_2_0
802                         [EditorBrowsable(EditorBrowsableState.Never)]
803 #endif
804                         public bool Contains(Image image)
805                         {
806                                 throw new NotSupportedException();
807                         }
808
809 #if NET_2_0
810                         public bool ContainsKey(string key)
811                         {
812                                 return IndexOfKey(key) != -1;
813                         }
814 #endif
815
816                         public IEnumerator GetEnumerator()
817                         {
818                                 Image[] images = new Image[this.Count];
819                                 int index;
820
821                                 if (images.Length != 0) {
822                                         // Handle is created only when there are images.
823                                         CreateHandle();
824
825                                         for (index = 0; index < images.Length; index++)
826                                                 images[index] = (Image)((Image)list[index]).Clone();
827                                 }
828
829                                 return images.GetEnumerator();
830                         }
831
832 #if NET_2_0
833                         [EditorBrowsable(EditorBrowsableState.Never)]
834 #endif
835                         public int IndexOf(Image image)
836                         {
837                                 throw new NotSupportedException();
838                         }
839
840 #if NET_2_0
841                         public int IndexOfKey(string key)
842                         {
843                                 int index;
844
845                                 if (key != null && key.Length != 0) {
846                                         // When last IndexOfKey was successful and the same key was
847                                         // assigned to an image with a lower index than the last
848                                         // result and the key of the last result equals to key
849                                         // argument the last result is returned.
850
851                                         if (this.lastKeyIndex >= 0 && this.lastKeyIndex < this.Count && CompareKeys((string)keys[this.lastKeyIndex], key))
852                                                 return this.lastKeyIndex;
853
854                                         // Duplicate keys are allowed and first match is returned.
855                                         for (index = 0; index < this.Count; index++)
856                                                 if (CompareKeys((string)keys[index], key))
857                                                         return this.lastKeyIndex = index;
858                                 }
859
860                                 return this.lastKeyIndex = -1;
861                         }
862 #endif
863
864 #if NET_2_0
865                         [EditorBrowsable(EditorBrowsableState.Never)]
866 #endif
867                         public void Remove(Image image)
868                         {
869                                 throw new NotSupportedException();
870                         }
871
872                         public void RemoveAt(int index)
873                         {
874                                 if (index < 0 || index >= this.Count)
875                                         throw new ArgumentOutOfRangeException("index");
876
877                                 CreateHandle();
878                                 list.RemoveAt(index);
879 #if NET_2_0
880                                 keys.RemoveAt(index);
881 #endif
882                         }
883
884 #if NET_2_0
885                         public void RemoveByKey(string key)
886                         {
887                                 int index;
888
889                                 if ((index = IndexOfKey(key)) != -1)
890                                         RemoveAt(index);
891                         }
892
893                         public void SetKeyName(int index, string name)
894                         {
895                                 // Only SetKeyName throws IndexOutOfRangeException.
896                                 if (index < 0 || index >= this.Count)
897                                         throw new IndexOutOfRangeException();
898
899                                 keys[index] = name;
900                         }
901 #endif
902                         #endregion // ImageCollection Public Instance Methods
903
904                         #region ImageCollection Interface Properties
905                         object IList.this[int index] {
906                                 get {
907                                         return this[index];
908                                 }
909
910                                 set {
911                                         if (!(value is Image))
912                                                 throw new ArgumentException("value");
913
914                                         this[index] = (Image)value;
915                                 }
916                         }
917
918                         bool IList.IsFixedSize {
919                                 get {
920                                         return false;
921                                 }
922                         }
923
924                         bool ICollection.IsSynchronized {
925                                 get {
926                                         return false;
927                                 }
928                         }
929
930                         object ICollection.SyncRoot {
931                                 get {
932                                         return this;
933                                 }
934                         }
935                         #endregion // ImageCollection Interface Properties
936
937                         #region ImageCollection Interface Methods
938                         int IList.Add(object value)
939                         {
940                                 int index;
941
942                                 if (!(value is Image))
943                                         throw new ArgumentException("value");
944
945                                 index = this.Count;
946                                 this.Add((Image)value);
947                                 return index;
948                         }
949
950                         bool IList.Contains(object value)
951                         {
952                                 return value is Image ? this.Contains((Image)value) : false;
953                         }
954
955                         int IList.IndexOf(object value)
956                         {
957                                 return value is Image ? this.IndexOf((Image)value) : -1;
958                         }
959
960                         void IList.Insert(int index, object value)
961                         {
962                                 throw new NotSupportedException();
963                         }
964
965                         void IList.Remove(object value)
966                         {
967                                 if (value is Image)
968                                         this.Remove((Image)value);
969                         }
970
971                         void ICollection.CopyTo(Array array, int index)
972                         {
973                                 int imageIndex;
974
975                                 for (imageIndex = 0; imageIndex < this.Count; imageIndex++)
976                                         array.SetValue(this[index], index++);
977                         }
978                         #endregion // ImageCollection Interface Methods
979                 }
980                 #endregion // Sub-classes
981
982                 #region Public Constructors
983                 public ImageList()
984                 {
985                         images = new ImageCollection(this);
986                 }
987
988                 public ImageList(System.ComponentModel.IContainer container) : this()
989                 {
990                         container.Add(this);
991                 }
992                 #endregion // Public Constructors
993
994                 #region Private Instance Methods
995                 private void OnRecreateHandle()
996                 {
997                         if (recreateHandle != null)
998                                 recreateHandle(this, EventArgs.Empty);
999                 }
1000
1001 #if NET_2_0
1002                 // For use in Designers
1003                 private void ResetColorDepth()
1004                 {
1005                         this.ColorDepth = DefaultColorDepth;
1006                 }
1007
1008                 // For use in Designers
1009                 private void ResetImageSize()
1010                 {
1011                         this.ImageSize = DefaultImageSize;
1012                 }
1013
1014                 // For use in Designers
1015                 private void ResetTransparentColor()
1016                 {
1017                         this.TransparentColor = DefaultTransparentColor;
1018                 }
1019
1020                 // For use in Designers
1021                 private bool ShouldSerializeColorDepth()
1022                 {
1023                         // ColorDepth is serialized in ImageStream when non-empty.
1024                         // It is serialized even if it has its default value when empty.
1025                         return images.Empty;
1026                 }
1027
1028                 // For use in Designers
1029                 private bool ShouldSerializeImageSize()
1030                 {
1031                         // ImageSize is serialized in ImageStream when non-empty.
1032                         // It is serialized even if it has its default value when empty.
1033                         return images.Empty;
1034                 }
1035
1036                 // For use in Designers
1037                 private bool ShouldSerializeTransparentColor()
1038                 {
1039                         return this.TransparentColor != DefaultTransparentColor;
1040                 }
1041 #endif
1042                 #endregion // Private Instance Methods
1043
1044                 #region Public Instance Properties
1045 #if !NET_2_0
1046                 [DefaultValue(DefaultColorDepth)]
1047 #endif
1048                 public ColorDepth ColorDepth {
1049                         get {
1050                                 return images.ColorDepth;
1051                         }
1052
1053                         set {
1054                                 images.ColorDepth = value;
1055                         }
1056                 }
1057
1058                 [Browsable(false)]
1059                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1060                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1061                 public IntPtr Handle {
1062                         get {
1063                                 return images.Handle;
1064                         }
1065                 }
1066
1067                 [Browsable(false)]
1068                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1069                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1070                 public bool HandleCreated {
1071                         get {
1072                                 return images.HandleCreated;
1073                         }
1074                 }
1075
1076                 [DefaultValue(null)]
1077                 [MergableProperty(false)]
1078                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1079                 public ImageCollection Images {
1080                         get {
1081                                 return this.images;
1082                         }
1083                 }
1084
1085                 [Localizable(true)]
1086                 public Size ImageSize {
1087                         get {
1088                                 return images.ImageSize;
1089                         }
1090
1091                         set {
1092                                 images.ImageSize = value;
1093                         }
1094                 }
1095
1096                 [Browsable(false)]
1097                 [DefaultValue(null)]
1098                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1099                 public ImageListStreamer ImageStream {
1100                         get {
1101                                 return images.ImageStream;
1102                         }
1103
1104                         set {
1105                                 images.ImageStream = value;
1106                         }
1107                 }
1108
1109 #if NET_2_0
1110                 [Bindable(true)]
1111                 [DefaultValue(null)]
1112                 [Localizable(false)]
1113                 [TypeConverter(typeof(StringConverter))]
1114                 public object Tag {
1115                         get {
1116                                 return this.tag;
1117                         }
1118
1119                         set {
1120                                 this.tag = value;
1121                         }
1122                 }
1123 #endif
1124
1125                 public Color TransparentColor {
1126                         get {
1127                                 return images.TransparentColor;
1128                         }
1129
1130                         set {
1131                                 images.TransparentColor = value;
1132                         }
1133                 }
1134                 #endregion // Public Instance Properties
1135
1136                 #region Public Instance Methods
1137                 public void Draw(Graphics g, Point pt, int index)
1138                 {
1139                         this.Draw(g, pt.X, pt.Y, index);
1140                 }
1141
1142                 public void Draw(Graphics g, int x, int y, int index)
1143                 {
1144                         g.DrawImage(images.GetImage(index), x, y);
1145                 }
1146
1147                 public void Draw(Graphics g, int x, int y, int width, int height, int index)
1148                 {
1149                         g.DrawImage(images.GetImage(index), x, y, width, height);
1150                 }
1151
1152                 public override string ToString()
1153                 {
1154                         return base.ToString() + " Images.Count: " + images.Count.ToString() + ", ImageSize: " + images.ImageSize.ToString();
1155                 }
1156                 #endregion // Public Instance Methods
1157
1158                 #region Protected Instance Methods
1159                 protected override void Dispose(bool disposing)
1160                 {
1161                         if (disposing)
1162                                 images.DestroyHandle();
1163
1164                         base.Dispose(disposing);
1165                 }
1166                 #endregion // Protected Instance Methods
1167
1168                 #region Events
1169                 [Browsable(false)]
1170                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1171                 public event EventHandler RecreateHandle {
1172                         add {
1173                                 recreateHandle += value;
1174                         }
1175
1176                         remove {
1177                                 recreateHandle -= value;
1178                         }
1179                 }
1180                 #endregion // Events
1181         }
1182 }