Update mcs/class/System.Core/System/TimeZoneInfo.cs
[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 does not clear keys when handle is destroyed that is treated as
60 // a bug.
61 //
62
63 using System.Collections;
64 using System.Collections.Specialized;
65 using System.ComponentModel;
66 using System.ComponentModel.Design.Serialization;
67 using System.Drawing;
68 using System.Drawing.Design;
69 using System.Drawing.Imaging;
70 using System.Globalization;
71 using System.Runtime.InteropServices;
72
73 namespace System.Windows.Forms
74 {
75         [DefaultProperty("Images")]
76         [Designer("System.Windows.Forms.Design.ImageListDesigner, " + Consts.AssemblySystem_Design)]
77         [DesignerSerializer("System.Windows.Forms.Design.ImageListCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
78         [ToolboxItemFilter("System.Windows.Forms")]
79         [TypeConverter(typeof(ImageListConverter))]
80         public sealed class ImageList : System.ComponentModel.Component
81         {
82                 #region Private Fields
83                 private const ColorDepth DefaultColorDepth = ColorDepth.Depth8Bit;
84                 private static readonly Size DefaultImageSize = new Size(16, 16);
85                 private static readonly Color DefaultTransparentColor = Color.Transparent;
86                 private object tag;
87                 private readonly ImageCollection images;
88                 #endregion // Private Fields
89
90                 #region Sub-classes
91                 [Editor("System.Windows.Forms.Design.ImageCollectionEditor, " + Consts.AssemblySystem_Design, typeof(UITypeEditor))]
92                 public sealed class ImageCollection : IList, ICollection, IEnumerable
93                 {
94                         private const int AlphaMask = unchecked((int)0xFF000000);
95
96                         private static class IndexedColorDepths
97                         {
98                                 internal static readonly ColorPalette Palette4Bit;
99                                 internal static readonly ColorPalette Palette8Bit;
100                                 private static readonly int[] squares;
101
102                                 static IndexedColorDepths()
103                                 {
104                                         Bitmap bitmap;
105                                         int index;
106
107                                         bitmap = new Bitmap(1, 1, PixelFormat.Format4bppIndexed);
108                                         Palette4Bit = bitmap.Palette;
109                                         bitmap.Dispose();
110
111                                         bitmap = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
112                                         Palette8Bit = bitmap.Palette;
113                                         bitmap.Dispose();
114
115                                         squares = new int[511];
116                                         for (index = 0; index < 256; index++)
117                                                 squares[255 + index] = squares[255 - index] = index * index;
118                                 }
119
120                                 internal static int GetNearestColor(Color[] palette, int color)
121                                 {
122                                         int index;
123                                         int count;
124                                         int red;
125                                         int green;
126                                         int blue;
127                                         int nearestColor;
128                                         int minDistance;
129                                         int distance;
130
131                                         count = palette.Length;
132                                         for (index = 0; index < count; index++)
133                                                 if (palette[index].ToArgb() == color)
134                                                         return color;
135
136                                         red = unchecked((int)(unchecked((uint)color) >> 16) & 0xFF);
137                                         green = unchecked((int)(unchecked((uint)color) >> 8) & 0xFF);
138                                         blue = color & 0xFF;
139                                         nearestColor = AlphaMask;
140                                         minDistance = int.MaxValue;
141
142                                         for (index = 0; index < count; index++)
143                                                 if ((distance = squares[255 + palette[index].R - red] + squares[255 + palette[index].G - green] + squares[255 + palette[index].B - blue]) < minDistance) {
144                                                         nearestColor = palette[index].ToArgb();
145                                                         minDistance = distance;
146                                                 }
147
148                                         return nearestColor;
149                                 }
150                         }
151
152                         [Flags()]
153                         private enum ItemFlags
154                         {
155                                 None = 0,
156                                 UseTransparentColor = 1,
157                                 ImageStrip = 2
158                         }
159
160                         private sealed class ImageListItem
161                         {
162                                 internal readonly object Image;
163                                 internal readonly ItemFlags Flags;
164                                 internal readonly Color TransparentColor;
165                                 internal readonly int ImageCount = 1;
166
167                                 internal ImageListItem(Icon value)
168                                 {
169                                         if (value == null)
170                                                 throw new ArgumentNullException("value");
171
172                                         // Icons are cloned.
173                                         this.Image = (Icon)value.Clone();
174                                 }
175
176                                 internal ImageListItem(Image value)
177                                 {
178                                         if (value == null)
179                                                 throw new ArgumentNullException("value");
180
181                                         if (!(value is Bitmap))
182                                                 throw new ArgumentException("Image must be a Bitmap.");
183
184                                         // Images are not cloned.
185                                         this.Image = value;
186                                 }
187
188                                 internal ImageListItem(Image value, Color transparentColor) : this(value)
189                                 {
190                                         this.Flags = ItemFlags.UseTransparentColor;
191                                         this.TransparentColor = transparentColor;
192                                 }
193
194                                 internal ImageListItem(Image value, int imageCount) : this(value)
195                                 {
196                                         this.Flags = ItemFlags.ImageStrip;
197                                         this.ImageCount = imageCount;
198                                 }
199                         }
200
201                         #region ImageCollection Private Fields
202                         private ColorDepth colorDepth = DefaultColorDepth;
203                         private Size imageSize = DefaultImageSize;
204                         private Color transparentColor = DefaultTransparentColor;
205                         private ArrayList list = new ArrayList();
206                         private ArrayList keys = new ArrayList();
207                         private int count;
208                         private bool handleCreated;
209                         private int lastKeyIndex = -1;
210                         private readonly ImageList owner;
211                         #endregion // ImageCollection Private Fields
212
213                         #region ImageCollection Internal Constructors
214                         // For use in ImageList
215                         internal ImageCollection(ImageList owner)
216                         {
217                                 this.owner = owner;
218                         }
219                         #endregion // ImageCollection Internal Constructor
220
221                         #region ImageCollection Internal Instance Properties
222                         // For use in ImageList
223                         internal ColorDepth ColorDepth {
224                                 get {
225                                         return this.colorDepth;
226                                 }
227
228                                 set {
229                                         if (!Enum.IsDefined(typeof(ColorDepth), value))
230                                                 throw new InvalidEnumArgumentException("value", (int)value, typeof(ColorDepth));
231
232                                         if (this.colorDepth != value) {
233                                                 this.colorDepth = value;
234                                                 RecreateHandle();
235                                         }
236                                 }
237                         }
238
239                         // For use in ImageList
240                         internal IntPtr Handle {
241                                 get {
242                                         CreateHandle();
243                                         return (IntPtr)(-1);
244                                 }
245                         }
246
247                         // For use in ImageList
248                         internal bool HandleCreated {
249                                 get {
250                                         return this.handleCreated;
251                                 }
252                         }
253
254                         // For use in ImageList
255                         internal Size ImageSize {
256                                 get {
257                                         return this.imageSize;
258                                 }
259
260                                 set {
261                                         if (value.Width < 1 || value.Width > 256 || value.Height < 1 || value.Height > 256)
262                                                 throw new ArgumentException("ImageSize.Width and Height must be between 1 and 256", "value");
263
264                                         if (this.imageSize != value) {
265                                                 this.imageSize = value;
266                                                 RecreateHandle();
267                                         }
268                                 }
269                         }
270
271                         // For use in ImageList
272                         internal ImageListStreamer ImageStream {
273                                 get {
274                                         return this.Empty ? null : new ImageListStreamer(this);
275                                 }
276
277                                 set {
278                                         int index;
279                                         Image[] streamImages;
280
281                                         if (value == null) {
282                                                 if (this.handleCreated)
283                                                         DestroyHandle();
284                                                 else
285                                                         this.Clear();
286                                         }
287                                         // Only deserialized ImageListStreamers are used.
288                                         else if ((streamImages = value.Images) != null) {
289                                                 this.list = new ArrayList(streamImages.Length);
290                                                 this.count = 0;
291                                                 this.handleCreated = true;
292                                                 this.keys = new ArrayList(streamImages.Length);
293
294                                                 for (index = 0; index < streamImages.Length; index++) {
295                                                         list.Add((Image)streamImages[index].Clone());
296                                                         keys.Add(null);
297                                                 }
298
299                                                 // Invalid ColorDepth values are ignored.
300                                                 if (Enum.IsDefined(typeof(ColorDepth), value.ColorDepth))
301                                                         this.colorDepth = (ColorDepth)value.ColorDepth;
302                                                 this.imageSize = value.ImageSize;
303
304                                                 // Event is raised even when handle was not created yet.
305                                                 owner.OnRecreateHandle();
306                                         }
307                                 }
308                         }
309
310                         // For use in ImageList
311                         internal Color TransparentColor {
312                                 get {
313                                         return this.transparentColor;
314                                 }
315
316                                 set {
317                                         this.transparentColor = value;
318                                 }
319                         }
320                         #endregion // ImageCollection Internal Instance Properties
321
322                         #region ImageCollection Public Instance Properties
323                         [Browsable(false)]
324                         public int Count {
325                                 get {
326                                         return this.handleCreated ? list.Count : this.count;
327                                 }
328                         }
329
330                         public bool Empty {
331                                 get {
332                                         return this.Count == 0;
333                                 }
334                         }
335
336                         public bool IsReadOnly {
337                                 get {
338                                         return false;
339                                 }
340                         }
341
342                         [Browsable(false)]
343                         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
344                         public Image this[int index] {
345                                 get {
346                                         return (Image)GetImage(index).Clone();
347                                 }
348
349                                 set {
350                                         Image image;
351
352                                         if (index < 0 || index >= this.Count)
353                                                 throw new ArgumentOutOfRangeException("index");
354
355                                         if (value == null)
356                                                 throw new ArgumentNullException("value");
357
358                                         if (!(value is Bitmap))
359                                                 throw new ArgumentException("Image must be a Bitmap.");
360
361                                         image = CreateImage(value, this.transparentColor);
362                                         CreateHandle();
363                                         list[index] = image;
364                                 }
365                         }
366
367                         public Image this[string key] {
368                                 get {
369                                         int index;
370
371                                         return (index = IndexOfKey(key)) == -1 ? null : this[index];
372                                 }
373                         }
374
375                         public StringCollection Keys {
376                                 get {
377                                         int index;
378                                         string key;
379                                         StringCollection keyCollection;
380
381                                         // Returns all keys even when there are more keys than
382                                         // images. Null keys are returned as empty strings.
383
384                                         keyCollection = new StringCollection();
385                                         for (index = 0; index < keys.Count; index++)
386                                                 keyCollection.Add(((key = (string)keys[index]) == null || key.Length == 0) ? string.Empty : key);
387
388                                         return keyCollection;
389                                 }
390                         }
391                         #endregion // ImageCollection Public Instance Properties
392
393                         #region ImageCollection Private Static Methods
394                         private static bool CompareKeys(string key1, string key2)
395                         {
396                                 // Keys are case-insensitive and keys with different length
397                                 // are not equal even when string.Compare treats them equal.
398
399                                 if (key1 == null || key2 == null || key1.Length != key2.Length)
400                                         return false;
401
402                                 return string.Compare(key1, key2, true, CultureInfo.InvariantCulture) == 0;
403                         }
404                         #endregion // ImageCollection Private Static Methods
405
406                         #region ImageCollection Private Instance Methods
407                         private int AddItem(string key, ImageListItem item)
408                         {
409                                 int itemIndex;
410                                 int index;
411
412                                 if (this.handleCreated)
413                                         itemIndex = AddItemInternal(item);
414                                 else {
415                                         // Image strips are counted as a single item in the return
416                                         // value of Add and AddStrip until handle is created.
417
418                                         itemIndex = list.Add(item);
419                                         this.count += item.ImageCount;
420                                 }
421
422                                 if ((item.Flags & ItemFlags.ImageStrip) == 0)
423                                         keys.Add(key);
424                                 else
425                                         for (index = 0; index < item.ImageCount; index++)
426                                                 keys.Add(null);
427
428                                 return itemIndex;
429                         }
430
431                         internal event EventHandler Changed;
432
433                         private int AddItemInternal(ImageListItem item)
434                         {
435                                 if (Changed != null)
436                                         Changed (this, EventArgs.Empty);
437
438                                 if (item.Image is Icon) {
439                                         int imageWidth;
440                                         int imageHeight;
441                                         Bitmap bitmap;
442                                         Graphics graphics;
443
444                                         bitmap = new Bitmap(imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
445                                         graphics = Graphics.FromImage(bitmap);
446                                         graphics.DrawIcon((Icon)item.Image, new Rectangle(0, 0, imageWidth, imageHeight));
447                                         graphics.Dispose();
448
449                                         ReduceColorDepth(bitmap);
450                                         return list.Add(bitmap);
451                                 }
452                                 else if ((item.Flags & ItemFlags.ImageStrip) == 0)
453                                         return list.Add(CreateImage((Image)item.Image, (item.Flags & ItemFlags.UseTransparentColor) == 0 ? this.transparentColor : item.TransparentColor));
454                                 else {
455                                         int imageX;
456                                         int width;
457                                         int imageWidth;
458                                         int imageHeight;
459                                         int index;
460                                         Image image;
461                                         Bitmap bitmap;
462                                         Graphics graphics;
463                                         Rectangle imageRect;
464                                         ImageAttributes imageAttributes;
465
466                                         // When ImageSize was changed after adding image strips
467                                         // Count will return invalid values based on old ImageSize
468                                         // but when creating handle either ArgumentException will
469                                         // be thrown or image strip will be added according to the
470                                         // new ImageSize. This can result in image count
471                                         // difference that can result in exceptions in methods
472                                         // that use Count before creating handle. In addition this
473                                         // can result in the loss of sync with keys. When doing
474                                         // the same after handle was created there are no problems
475                                         // as handle will be recreated after changing ImageSize
476                                         // that results in the loss of images added previously.
477
478                                         if ((width = (image = (Image)item.Image).Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
479                                                 throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
480
481                                         if (image.Height != (imageHeight = this.imageSize.Height))
482                                                 throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
483
484                                         imageRect = new Rectangle(0, 0, imageWidth, imageHeight);
485                                         if (this.transparentColor.A == 0)
486                                                 imageAttributes = null;
487                                         else {
488                                                 imageAttributes = new ImageAttributes();
489                                                 imageAttributes.SetColorKey(this.transparentColor, this.transparentColor);
490                                         }
491
492                                         index = list.Count;
493                                         for (imageX = 0; imageX < width; imageX += imageWidth) {
494                                                 bitmap = new Bitmap(imageWidth, imageHeight, PixelFormat.Format32bppArgb);
495                                                 graphics = Graphics.FromImage(bitmap);
496                                                 graphics.DrawImage(image, imageRect, imageX, 0, imageWidth, imageHeight, GraphicsUnit.Pixel, imageAttributes);
497                                                 graphics.Dispose();
498
499                                                 ReduceColorDepth(bitmap);
500                                                 list.Add(bitmap);
501                                         }
502
503                                         if (imageAttributes != null)
504                                                 imageAttributes.Dispose();
505
506                                         return index;
507                                 }
508                         }
509
510                         private void CreateHandle()
511                         {
512                                 int index;
513                                 ArrayList items;
514
515                                 if (!this.handleCreated) {
516                                         items = this.list;
517                                         this.list = new ArrayList(this.count);
518                                         this.count = 0;
519                                         this.handleCreated = true;
520
521                                         for (index = 0; index < items.Count; index++)
522                                                 AddItemInternal((ImageListItem)items[index]);
523                                 }
524                         }
525
526                         private Image CreateImage(Image value, Color transparentColor)
527                         {
528                                 int imageWidth;
529                                 int imageHeight;
530                                 ImageAttributes imageAttributes;
531
532                                 if (transparentColor.A == 0)
533                                         imageAttributes = null;
534                                 else {
535                                         imageAttributes = new ImageAttributes();
536                                         imageAttributes.SetColorKey (transparentColor, transparentColor);
537                                 }
538
539                                 var bitmap = new Bitmap (imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
540                                 using (var graphics = Graphics.FromImage (bitmap))
541                                         graphics.DrawImage (value, new Rectangle(0, 0, imageWidth, imageHeight), 0, 0, value.Width, value.Height, GraphicsUnit.Pixel, imageAttributes);
542
543                                 if (imageAttributes != null)
544                                         imageAttributes.Dispose ();
545
546                                 ReduceColorDepth (bitmap);
547                                 return bitmap;
548                         }
549
550                         private void RecreateHandle()
551                         {
552                                 if (this.handleCreated) {
553                                         DestroyHandle();
554                                         this.handleCreated = true;
555                                         owner.OnRecreateHandle();
556                                 }
557                         }
558
559                         private unsafe void ReduceColorDepth(Bitmap bitmap)
560                         {
561                                 byte* pixelPtr;
562                                 byte* lineEndPtr;
563                                 byte* linePtr;
564                                 int line;
565                                 int pixel;
566                                 int height;
567                                 int widthBytes;
568                                 int stride;
569                                 BitmapData bitmapData;
570                                 Color[] palette;
571
572                                 if (this.colorDepth < ColorDepth.Depth32Bit) {
573                                         bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
574                                         try {
575                                                 linePtr = (byte*)bitmapData.Scan0;
576                                                 height = bitmapData.Height;
577                                                 widthBytes = bitmapData.Width << 2;
578                                                 stride = bitmapData.Stride;
579
580                                                 if (this.colorDepth < ColorDepth.Depth16Bit) {
581                                                         palette = (this.colorDepth < ColorDepth.Depth8Bit ? IndexedColorDepths.Palette4Bit : IndexedColorDepths.Palette8Bit).Entries;
582
583                                                         for (line = 0; line < height; line++) {
584                                                                 lineEndPtr = linePtr + widthBytes;
585                                                                 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
586                                                                         *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : IndexedColorDepths.GetNearestColor(palette, pixel | AlphaMask);
587                                                                 linePtr += stride;
588                                                         }
589                                                 }
590                                                 else if (this.colorDepth < ColorDepth.Depth24Bit) {
591                                                         for (line = 0; line < height; line++) {
592                                                                 lineEndPtr = linePtr + widthBytes;
593                                                                 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
594                                                                         *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : (pixel & 0x00F8F8F8) | AlphaMask;
595                                                                 linePtr += stride;
596                                                         }
597                                                 }
598                                                 else {
599                                                         for (line = 0; line < height; line++) {
600                                                                 lineEndPtr = linePtr + widthBytes;
601                                                                 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
602                                                                         *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : pixel | AlphaMask;
603                                                                 linePtr += stride;
604                                                         }
605                                                 }
606                                         }
607                                         finally {
608                                                 bitmap.UnlockBits(bitmapData);
609                                         }
610                                 }
611                         }
612                         #endregion // ImageCollection Private Instance Methods
613
614                         #region ImageCollection Internal Instance Methods
615                         // For use in ImageList
616                         internal void DestroyHandle()
617                         {
618                                 if (this.handleCreated) {
619                                         this.list = new ArrayList();
620                                         this.count = 0;
621                                         this.handleCreated = false;
622                                         keys = new ArrayList();
623                                 }
624                         }
625
626                         // For use in ImageList
627                         internal Image GetImage(int index)
628                         {
629                                 if (index < 0 || index >= this.Count)
630                                         throw new ArgumentOutOfRangeException("index");
631
632                                 CreateHandle();
633                                 return (Image)list[index];
634                         }
635
636                         // For use in ImageListStreamer
637                         internal Image[] ToArray()
638                         {
639                                 Image[] images;
640
641                                 // Handle is created even when the list is empty.
642                                 CreateHandle();
643                                 images = new Image[list.Count];
644                                 list.CopyTo(images);
645                                 return images;
646                         }
647                         #endregion // ImageCollection Internal Instance Methods
648
649                         #region ImageCollection Public Instance Methods
650                         public void Add(Icon value)
651                         {
652                                 Add(null, value);
653                         }
654
655                         public void Add(Image value)
656                         {
657                                 Add(null, value);
658                         }
659
660                         public int Add(Image value, Color transparentColor)
661                         {
662                                 return AddItem(null, new ImageListItem(value, transparentColor));
663                         }
664
665                         public void Add(string key, Icon icon)
666                         {
667                                 // Argument has name icon but exceptions use name value.
668                                 AddItem(key, new ImageListItem(icon));
669                         }
670
671                         public void Add(string key, Image image)
672                         {
673                                 // Argument has name image but exceptions use name value.
674                                 AddItem(key, new ImageListItem(image));
675                         }
676
677                         public void AddRange(Image[] images)
678                         {
679                                 int index;
680
681                                 if (images == null)
682                                         throw new ArgumentNullException("images");
683
684                                 for (index = 0; index < images.Length; index++)
685                                         Add(images[index]);
686                         }
687
688                         public int AddStrip(Image value)
689                         {
690                                 int width;
691                                 int imageWidth;
692
693                                 if (value == null)
694                                         throw new ArgumentNullException("value");
695
696                                 if ((width = value.Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
697                                         throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
698
699                                 if (value.Height != this.imageSize.Height)
700                                         throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
701
702                                 return AddItem(null, new ImageListItem(value, width / imageWidth));
703                         }
704
705                         public void Clear()
706                         {
707                                 list.Clear();
708                                 if (this.handleCreated)
709                                         this.count = 0;
710                                 keys.Clear();
711                         }
712
713                         [EditorBrowsable(EditorBrowsableState.Never)]
714                         public bool Contains(Image image)
715                         {
716                                 throw new NotSupportedException();
717                         }
718
719                         public bool ContainsKey(string key)
720                         {
721                                 return IndexOfKey(key) != -1;
722                         }
723
724                         public IEnumerator GetEnumerator()
725                         {
726                                 Image[] images = new Image[this.Count];
727                                 int index;
728
729                                 if (images.Length != 0) {
730                                         // Handle is created only when there are images.
731                                         CreateHandle();
732
733                                         for (index = 0; index < images.Length; index++)
734                                                 images[index] = (Image)((Image)list[index]).Clone();
735                                 }
736
737                                 return images.GetEnumerator();
738                         }
739
740                         [EditorBrowsable(EditorBrowsableState.Never)]
741                         public int IndexOf(Image image)
742                         {
743                                 throw new NotSupportedException();
744                         }
745
746                         public int IndexOfKey(string key)
747                         {
748                                 int index;
749
750                                 if (key != null && key.Length != 0) {
751                                         // When last IndexOfKey was successful and the same key was
752                                         // assigned to an image with a lower index than the last
753                                         // result and the key of the last result equals to key
754                                         // argument the last result is returned.
755
756                                         if (this.lastKeyIndex >= 0 && this.lastKeyIndex < this.Count && CompareKeys((string)keys[this.lastKeyIndex], key))
757                                                 return this.lastKeyIndex;
758
759                                         // Duplicate keys are allowed and first match is returned.
760                                         for (index = 0; index < this.Count; index++)
761                                                 if (CompareKeys((string)keys[index], key))
762                                                         return this.lastKeyIndex = index;
763                                 }
764
765                                 return this.lastKeyIndex = -1;
766                         }
767
768                         [EditorBrowsable(EditorBrowsableState.Never)]
769                         public void Remove(Image image)
770                         {
771                                 throw new NotSupportedException();
772                         }
773
774                         public void RemoveAt(int index)
775                         {
776                                 if (index < 0 || index >= this.Count)
777                                         throw new ArgumentOutOfRangeException("index");
778
779                                 CreateHandle();
780                                 list.RemoveAt(index);
781                                 keys.RemoveAt(index);
782                                 if (Changed != null)
783                                         Changed (this, EventArgs.Empty);
784                         }
785
786                         public void RemoveByKey(string key)
787                         {
788                                 int index;
789
790                                 if ((index = IndexOfKey(key)) != -1)
791                                         RemoveAt(index);
792                         }
793
794                         public void SetKeyName(int index, string name)
795                         {
796                                 // Only SetKeyName throws IndexOutOfRangeException.
797                                 if (index < 0 || index >= this.Count)
798                                         throw new IndexOutOfRangeException();
799
800                                 keys[index] = name;
801                         }
802                         #endregion // ImageCollection Public Instance Methods
803
804                         #region ImageCollection Interface Properties
805                         object IList.this[int index] {
806                                 get {
807                                         return this[index];
808                                 }
809
810                                 set {
811                                         if (!(value is Image))
812                                                 throw new ArgumentException("value");
813
814                                         this[index] = (Image)value;
815                                 }
816                         }
817
818                         bool IList.IsFixedSize {
819                                 get {
820                                         return false;
821                                 }
822                         }
823
824                         bool ICollection.IsSynchronized {
825                                 get {
826                                         return false;
827                                 }
828                         }
829
830                         object ICollection.SyncRoot {
831                                 get {
832                                         return this;
833                                 }
834                         }
835                         #endregion // ImageCollection Interface Properties
836
837                         #region ImageCollection Interface Methods
838                         int IList.Add(object value)
839                         {
840                                 int index;
841
842                                 if (!(value is Image))
843                                         throw new ArgumentException("value");
844
845                                 index = this.Count;
846                                 this.Add((Image)value);
847                                 return index;
848                         }
849
850                         bool IList.Contains(object image)
851                         {
852                                 return image is Image ? this.Contains ((Image) image) : false;
853                         }
854
855                         int IList.IndexOf (object image)
856                         {
857                                 return image is Image ? this.IndexOf ((Image) image) : -1;
858                         }
859
860                         void IList.Insert(int index, object value)
861                         {
862                                 throw new NotSupportedException();
863                         }
864
865                         void IList.Remove (object image)
866                         {
867                                 if (image is Image)
868                                         this.Remove ((Image) image);
869                         }
870
871                         void ICollection.CopyTo(Array dest, int index)
872                         {
873                                 for (int imageIndex = 0; imageIndex < this.Count; imageIndex++)
874                                         dest.SetValue (this[imageIndex], index++);
875                         }
876                         #endregion // ImageCollection Interface Methods
877                 }
878                 #endregion // Sub-classes
879
880                 #region Public Constructors
881                 public ImageList()
882                 {
883                         images = new ImageCollection(this);
884                 }
885
886                 public ImageList(System.ComponentModel.IContainer container) : this()
887                 {
888                         container.Add(this);
889                 }
890                 #endregion // Public Constructors
891
892                 #region Private Instance Methods
893                 private void OnRecreateHandle()
894                 {
895                         EventHandler eh = (EventHandler)(Events [RecreateHandleEvent]);
896                         if (eh != null)
897                                 eh (this, EventArgs.Empty);
898                 }
899
900                 // MS's TypeDescriptor stuff apparently uses
901                 // non-public ShouldSerialize* methods, because it
902                 // picks up this behavior even though the methods
903                 // aren't public.  we can't make them private, though,
904                 // without adding compiler warnings.  so, make then
905                 // internal instead.
906
907                 internal bool ShouldSerializeTransparentColor ()
908                 {
909                         return this.TransparentColor != Color.LightGray;
910                 }
911
912                 internal bool ShouldSerializeColorDepth()
913                 {
914                         // ColorDepth is serialized in ImageStream when non-empty.
915                         // It is serialized even if it has its default value when empty.
916                         return images.Empty;
917                 }               
918
919                 internal bool ShouldSerializeImageSize()
920                 {
921                         // ImageSize is serialized in ImageStream when non-empty.
922                         // It is serialized even if it has its default value when empty.
923                         return images.Empty;
924                 }
925
926                 internal void ResetColorDepth ()
927                 {
928                         this.ColorDepth = DefaultColorDepth;
929                 }
930
931                 internal void ResetImageSize ()
932                 {
933                         this.ImageSize = DefaultImageSize;
934                 }
935
936                 internal void ResetTransparentColor ()
937                 {
938                         this.TransparentColor = Color.LightGray;
939                 }
940                 #endregion // Private Instance Methods
941
942                 #region Public Instance Properties
943                 public ColorDepth ColorDepth {
944                         get {
945                                 return images.ColorDepth;
946                         }
947
948                         set {
949                                 images.ColorDepth = value;
950                         }
951                 }
952
953                 [Browsable(false)]
954                 [EditorBrowsable(EditorBrowsableState.Advanced)]
955                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
956                 public IntPtr Handle {
957                         get {
958                                 return images.Handle;
959                         }
960                 }
961
962                 [Browsable(false)]
963                 [EditorBrowsable(EditorBrowsableState.Advanced)]
964                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
965                 public bool HandleCreated {
966                         get {
967                                 return images.HandleCreated;
968                         }
969                 }
970
971                 [DefaultValue(null)]
972                 [MergableProperty(false)]
973                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
974                 public ImageCollection Images {
975                         get {
976                                 return this.images;
977                         }
978                 }
979
980                 [Localizable(true)]
981                 public Size ImageSize {
982                         get {
983                                 return images.ImageSize;
984                         }
985
986                         set {
987                                 images.ImageSize = value;
988                         }
989                 }
990
991                 [Browsable(false)]
992                 [DefaultValue(null)]
993                 [EditorBrowsable(EditorBrowsableState.Advanced)]
994                 public ImageListStreamer ImageStream {
995                         get {
996                                 return images.ImageStream;
997                         }
998
999                         set {
1000                                 images.ImageStream = value;
1001                         }
1002                 }
1003
1004                 [Bindable(true)]
1005                 [DefaultValue(null)]
1006                 [Localizable(false)]
1007                 [TypeConverter(typeof(StringConverter))]
1008                 public object Tag {
1009                         get {
1010                                 return this.tag;
1011                         }
1012
1013                         set {
1014                                 this.tag = value;
1015                         }
1016                 }
1017
1018                 public Color TransparentColor {
1019                         get {
1020                                 return images.TransparentColor;
1021                         }
1022
1023                         set {
1024                                 images.TransparentColor = value;
1025                         }
1026                 }
1027                 #endregion // Public Instance Properties
1028
1029                 #region Public Instance Methods
1030                 public void Draw(Graphics g, Point pt, int index)
1031                 {
1032                         this.Draw(g, pt.X, pt.Y, index);
1033                 }
1034
1035                 public void Draw(Graphics g, int x, int y, int index)
1036                 {
1037                         g.DrawImage(images.GetImage(index), x, y);
1038                 }
1039
1040                 public void Draw(Graphics g, int x, int y, int width, int height, int index)
1041                 {
1042                         g.DrawImage(images.GetImage(index), x, y, width, height);
1043                 }
1044
1045                 public override string ToString()
1046                 {
1047                         return base.ToString() + " Images.Count: " + images.Count.ToString() + ", ImageSize: " + this.ImageSize.ToString();
1048                 }
1049                 #endregion // Public Instance Methods
1050
1051                 #region Protected Instance Methods
1052                 protected override void Dispose(bool disposing)
1053                 {
1054                         if (disposing)
1055                                 images.DestroyHandle();
1056
1057                         base.Dispose(disposing);
1058                 }
1059                 #endregion // Protected Instance Methods
1060
1061                 #region Events
1062                 static object RecreateHandleEvent = new object ();
1063
1064                 [Browsable(false)]
1065                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1066                 public event EventHandler RecreateHandle {
1067                         add { Events.AddHandler (RecreateHandleEvent, value); }
1068                         remove { Events.RemoveHandler (RecreateHandleEvent, value); }
1069                 }
1070                 #endregion // Events
1071         }
1072 }