2007-03-29 Jonathan Pobst <monkey@jpobst.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ListViewItem.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004 Novell, Inc. (http://www.novell.com)
21 //
22 // Author:
23 //      Ravindra (rkumar@novell.com)
24 //      Mike Kestner <mkestner@novell.com>
25 //      Daniel Nauck (dna(at)mono-project(dot)de)
26
27 using System.Collections;
28 using System.ComponentModel;
29 using System.Drawing;
30 using System.Runtime.Serialization;
31
32 namespace System.Windows.Forms
33 {
34         [DefaultProperty ("Text")]
35         [DesignTimeVisible (false)]
36         [Serializable]
37         [ToolboxItem (false)]
38         [TypeConverter (typeof (ListViewItemConverter))]
39         public class ListViewItem : ICloneable, ISerializable
40         {
41                 #region Instance Variables
42                 private int image_index = -1;
43                 private bool is_checked = false;
44                 private bool is_focused = false;
45                 private int state_image_index = -1;
46                 private ListViewSubItemCollection sub_items;
47                 private object tag;
48                 private bool use_item_style = true;
49 #if NET_2_0
50                 private ListViewGroup group = null;
51                 private string name = String.Empty;
52                 private string image_key = String.Empty;
53                 int index;                      // cached index for VirtualMode
54 #endif
55                 Rectangle bounds;
56                 Rectangle checkbox_rect;        // calculated by CalcListViewItem method
57                 Rectangle icon_rect;
58                 Rectangle item_rect;
59                 Rectangle label_rect;
60                 ListView owner;
61                 Font font;
62                 bool selected;
63
64                 internal int row;
65                 internal int col;
66
67                 #endregion Instance Variables
68
69                 #region Public Constructors
70                 public ListViewItem ()
71                 {
72                         this.sub_items = new ListViewSubItemCollection (this);
73                         this.sub_items.Add ("");
74                 }
75
76                 public ListViewItem (string text) : this (text, -1)
77                 {
78                 }
79
80                 public ListViewItem (string [] items) : this (items, -1)
81                 {
82                 }
83
84                 public ListViewItem (ListViewItem.ListViewSubItem [] subItems, int imageIndex)
85                 {
86                         this.sub_items = new ListViewSubItemCollection (this);
87                         this.sub_items.AddRange (subItems);
88                         this.image_index = imageIndex;
89                 }
90
91                 public ListViewItem (string text, int imageIndex)
92                 {
93                         this.image_index = imageIndex;
94                         this.sub_items = new ListViewSubItemCollection (this);
95                         this.sub_items.Add (text);
96                 }
97
98                 public ListViewItem (string [] items, int imageIndex)
99                 {
100                         this.sub_items = new ListViewSubItemCollection (this);
101                         foreach (string item in items) // Don't use AddRange, since we need to add null strings
102                                 sub_items.Add (item);
103                         this.image_index = imageIndex;
104                 }
105
106                 public ListViewItem (string [] items, int imageIndex, Color foreColor, 
107                                      Color backColor, Font font)
108                 {
109                         this.sub_items = new ListViewSubItemCollection (this);
110                         foreach (string item in items)
111                                 sub_items.Add (item);
112                         this.image_index = imageIndex;
113                         ForeColor = foreColor;
114                         BackColor = backColor;
115                         this.font = font;
116                 }
117
118 #if NET_2_0
119                 public ListViewItem(string[] items, string imageKey) : this(items)
120                 {
121                         this.ImageKey = imageKey;
122                 }
123
124                 public ListViewItem(string text, string imageKey) : this(text)
125                 {
126                         this.ImageKey = imageKey;
127                 }
128
129                 public ListViewItem(ListViewSubItem[] subItems, string imageKey) : this()
130                 {
131                         this.sub_items.AddRange(subItems);
132                         this.ImageKey = imageKey;
133                 }
134
135                 public ListViewItem(string[] items, string imageKey, Color foreColor,
136                                         Color backColor, Font font) : this()
137                 {
138                         this.sub_items = new ListViewSubItemCollection(this);
139                         foreach (string item in items)
140                                 sub_items.Add (item);
141                         ForeColor = foreColor;
142                         BackColor = backColor;
143                         this.font = font;
144                         this.ImageKey = imageKey;
145                 }
146
147                 public ListViewItem(ListViewGroup group) : this()
148                 {
149                         this.group = group;
150                 }
151
152                 public ListViewItem(string text, ListViewGroup group) : this(text)
153                 {
154                         this.group = group;
155                 }
156
157                 public ListViewItem(string[] items, ListViewGroup group) : this(items)
158                 {
159                         this.group = group;
160                 }
161
162                 public ListViewItem(ListViewSubItem[] subItems, int imageIndex, ListViewGroup group)
163                         : this(subItems, imageIndex)
164                 {
165                         this.group = group;
166                 }
167
168                 public ListViewItem(ListViewSubItem[] subItems, string imageKey, ListViewGroup group)
169                         : this(subItems, imageKey)
170                 {
171                         this.group = group;
172                 }
173
174                 public ListViewItem(string text, int imageIndex, ListViewGroup group)
175                         : this(text, imageIndex)
176                 {
177                         this.group = group;
178                 }
179
180                 public ListViewItem(string text, string imageKey, ListViewGroup group)
181                         : this(text, imageKey)
182                 {
183                         this.group = group;
184                 }
185
186                 public ListViewItem(string[] items, int imageIndex, ListViewGroup group)
187                         : this(items, imageIndex)
188                 {
189                         this.group = group;
190                 }
191
192                 public ListViewItem(string[] items, string imageKey, ListViewGroup group)
193                         : this(items, imageKey)
194                 {
195                         this.group = group;
196                 }
197
198                 public ListViewItem(string[] items, int imageIndex, Color foreColor, Color backColor,
199                                 Font font, ListViewGroup group)
200                         : this(items, imageIndex, foreColor, backColor, font)
201                 {
202                         this.group = group;
203                 }
204
205                 public ListViewItem(string[] items, string imageKey, Color foreColor, Color backColor,
206                                 Font font, ListViewGroup group)
207                         : this(items, imageKey, foreColor, backColor, font)
208                 {
209                         this.group = group;
210                 }
211 #endif
212                 #endregion      // Public Constructors
213
214                 #region Public Instance Properties
215                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
216                 public Color BackColor {
217                         get {
218                                 if (sub_items.Count > 0)
219                                         return sub_items[0].BackColor;
220
221                                 if (owner != null)
222                                         return owner.BackColor;
223                                 
224                                 return ThemeEngine.Current.ColorWindow;
225                         }
226
227                         set { sub_items[0].BackColor = value; }
228                 }
229
230                 [Browsable (false)]
231                 public Rectangle Bounds {
232                         get {
233                                 return GetBounds (ItemBoundsPortion.Entire);
234                         }
235                 }
236
237                 [DefaultValue (false)]
238                 [RefreshProperties (RefreshProperties.Repaint)]
239                 public bool Checked {
240                         get { return is_checked; }
241                         set { 
242                                 if (is_checked == value)
243                                         return;
244                                 
245                                 if (owner != null) {
246                                         CheckState current_value = is_checked ? CheckState.Checked : CheckState.Unchecked;
247                                         CheckState new_value = value ? CheckState.Checked : CheckState.Unchecked;
248
249                                         ItemCheckEventArgs icea = new ItemCheckEventArgs (Index,
250                                                         new_value, current_value);
251                                         owner.OnItemCheck (icea);
252
253                                         if (new_value != current_value) {
254                                                 // force re-population of list
255                                                 owner.CheckedItems.Reset ();
256                                                 is_checked = new_value == CheckState.Checked;
257                                                 Invalidate ();
258
259 #if NET_2_0
260                                                 ItemCheckedEventArgs args = new ItemCheckedEventArgs (this);
261                                                 owner.OnItemChecked (args);
262 #endif
263                                         }
264                                 } else
265                                         is_checked = value;
266                         }
267                 }
268
269                 [Browsable (false)]
270                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
271                 public bool Focused {
272                         get { return is_focused; }
273                         set {   
274                                 if (is_focused == value)
275                                         return;
276
277                                 is_focused = value; 
278
279                                 Invalidate ();
280                                 if (owner != null)
281                                         Layout ();
282                                 Invalidate ();
283                         }
284                 }
285
286                 [Localizable (true)]
287                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
288                 public Font Font {
289                         get {
290                                 if (font != null)
291                                         return font;
292                                 else if (owner != null)
293                                         return owner.Font;
294
295                                 return ThemeEngine.Current.DefaultFont;
296                         }
297                         set {   
298                                 if (font == value)
299                                         return;
300
301                                 font = value; 
302
303                                 if (owner != null)
304                                         Layout ();
305                                 Invalidate ();
306                         }
307                 }
308
309                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
310                 public Color ForeColor {
311                         get {
312                                 if (sub_items.Count > 0)
313                                         return sub_items[0].ForeColor;
314
315                                 if (owner != null)
316                                         return owner.ForeColor;
317
318                                 return ThemeEngine.Current.ColorWindowText;
319                         }
320                         set { sub_items[0].ForeColor = value; }
321                 }
322
323                 [DefaultValue (-1)]
324                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
325                 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design,
326                          typeof (System.Drawing.Design.UITypeEditor))]
327                 [Localizable (true)]
328 #if NET_2_0
329                 [RefreshProperties (RefreshProperties.Repaint)]
330                 // [TypeConverter (typeof (NoneExcludedImageIndexConverter))]
331 #else
332                 [TypeConverter (typeof (ImageIndexConverter))]
333 #endif
334                 public int ImageIndex {
335                         get { return image_index; }
336                         set {
337                                 if (value < -1)
338                                         throw new ArgumentException ("Invalid ImageIndex. It must be greater than or equal to -1.");
339                                 
340                                 image_index = value;
341 #if NET_2_0
342                                 image_key = String.Empty;
343 #endif
344
345                                 if (owner != null)
346                                         Layout ();
347                                 Invalidate ();
348                         }
349                 }
350
351 #if NET_2_0
352                 [DefaultValue ("")]
353                 [LocalizableAttribute (true)]
354                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
355                 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design,
356                          typeof (System.Drawing.Design.UITypeEditor))]
357                 [RefreshProperties (RefreshProperties.Repaint)]
358                 // XXX [TypeConverter (typeof (ImageKeyConverter))
359                 public string ImageKey {
360                         get {
361                                 return image_key;
362                         }
363                         set {
364                                 image_key = value == null ? String.Empty : value;
365                                 image_index = -1;
366
367                                 if (owner != null)
368                                         Layout ();
369                                 Invalidate ();
370                         }
371                 }
372 #endif
373
374                 [Browsable (false)]
375                 public ImageList ImageList {
376                         get {
377                                 if (owner == null)
378                                         return null;
379                                 else if (owner.View == View.LargeIcon)
380                                         return owner.large_image_list;
381                                 else
382                                         return owner.small_image_list;
383                         }
384                 }
385
386                 [Browsable (false)]
387                 public int Index {
388                         get {
389                                 if (owner == null)
390                                         return -1;
391 #if NET_2_0
392                                 if (owner.VirtualMode)
393                                         return index;
394 #endif
395                                 else
396                                         return owner.Items.IndexOf (this);
397                         }
398                 }
399
400                 [Browsable (false)]
401                 public ListView ListView {
402                         get { return owner; }
403                 }
404
405 #if NET_2_0
406                 [Browsable (false)]
407                 [Localizable (true)]
408                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
409                 public string Name {
410                         get {
411                                 return name;
412                         }
413                         set {
414                                 name = value == null ? String.Empty : value;
415                         }
416                 }
417 #endif
418
419                 [Browsable (false)]
420                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
421                 public bool Selected {
422                         get { 
423                                 return selected; 
424                         }
425                         set {
426                                 if (selected == value)
427                                         return;
428
429                                 if (owner != null) {
430                                         if (value && !owner.MultiSelect)
431                                                 owner.SelectedItems.Clear ();
432                                         selected = value;
433                                         // force re-population of list
434                                         owner.SelectedIndices.Reset ();
435                                         Layout ();
436                                         owner.OnSelectedIndexChanged ();
437                                 } else {
438                                         selected = value;
439                                 }
440                                 Invalidate ();
441                         }
442                 }
443
444                 [DefaultValue (-1)]
445                 [Editor ("System.Windows.Forms.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design,
446                          typeof (System.Drawing.Design.UITypeEditor))]
447                 [Localizable (true)]
448 #if NET_2_0
449                 [RefreshProperties (RefreshProperties.Repaint)]
450                 // XXX [RelatedImageListAttribute ("ListView.StateImageList")]
451                 // XXX [TypeConverter (typeof (NoneExcludedImageIndexConverter))]
452 #else
453                 [TypeConverter (typeof (ImageIndexConverter))]
454 #endif
455                 public int StateImageIndex {
456                         get { return state_image_index; }
457                         set {
458                                 if (value < -1 || value > 14)
459                                         throw new ArgumentOutOfRangeException ("Invalid StateImageIndex. It must be in the range of [-1, 14].");
460
461                                 state_image_index = value;
462                         }
463                 }
464
465                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
466 #if NET_2_0
467                 [Editor ("System.Windows.Forms.Design.ListViewSubItemCollectionEditor, " + Consts.AssemblySystem_Design,
468                          typeof (System.Drawing.Design.UITypeEditor))]
469 #endif
470                 public ListViewSubItemCollection SubItems {
471                         get { return sub_items; }
472                 }
473
474                 [Bindable (true)]
475                 [DefaultValue (null)]
476                 [Localizable (false)]
477                 [TypeConverter (typeof (StringConverter))]
478                 public object Tag {
479                         get { return tag; }
480                         set { tag = value; }
481                 }
482
483                 [Localizable (true)]
484                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
485                 public string Text {
486                         get {
487                                 if (this.sub_items.Count > 0)
488                                         return this.sub_items [0].Text;
489                                 else
490                                         return string.Empty;
491                         }
492                         set { 
493                                 if (sub_items [0].Text == value)
494                                         return;
495
496                                 sub_items [0].Text = value; 
497
498                                 if (owner != null)
499                                         Layout ();
500                                 Invalidate ();
501                         }
502                 }
503
504                 [DefaultValue (true)]
505                 public bool UseItemStyleForSubItems {
506                         get { return use_item_style; }
507                         set { use_item_style = value; }
508                 }
509
510 #if NET_2_0
511                 [LocalizableAttribute(true)]
512                 [DefaultValue (null)]
513                 public ListViewGroup Group {
514                         get { return this.group; }
515                         set
516                         {
517                                 if (this.group != value)
518                                 {
519                                         if (value != null)
520                                         {
521                                                 value.Items.Add(this);
522
523                                                 if (this.group != null)
524                                                         this.group.Items.Remove(this);
525
526                                                 this.group = value;
527                                         }
528                                         else
529                                         {
530                                                 if(this.group != null)
531                                                 this.group.Items.Remove(this);
532                                         }
533                                 }
534                         }
535                 }
536 #endif
537
538                 #endregion      // Public Instance Properties
539
540                 #region Public Instance Methods
541                 public void BeginEdit ()
542                 {
543                         if (owner != null && owner.LabelEdit) {
544                                 owner.item_control.BeginEdit (this);
545                         }
546                         // FIXME: TODO
547                         // if (owner != null && owner.LabelEdit 
548                         //    && owner.Activation == ItemActivation.Standard)
549                         // allow editing
550                         // else
551                         // throw new InvalidOperationException ();
552                 }
553
554                 public virtual object Clone ()
555                 {
556                         ListViewItem clone = new ListViewItem ();
557                         clone.image_index = this.image_index;
558                         clone.is_checked = this.is_checked;
559                         clone.is_focused = this.is_focused;
560                         clone.selected = this.selected;
561                         clone.font = this.font;
562                         clone.state_image_index = this.state_image_index;
563                         clone.sub_items = new ListViewSubItemCollection (this);
564                         
565                         foreach (ListViewSubItem subItem in this.sub_items)
566                                 clone.sub_items.Add (subItem.Text, subItem.ForeColor,
567                                                      subItem.BackColor, subItem.Font);
568                         clone.tag = this.tag;
569                         clone.use_item_style = this.use_item_style;
570                         clone.owner = null;
571 #if NET_2_0
572                         clone.name = name;
573 #endif
574
575                         return clone;
576                 }
577
578                 public virtual void EnsureVisible ()
579                 {
580                         if (this.owner != null) {
581                                 owner.EnsureVisible (owner.Items.IndexOf (this));
582                         }
583                 }
584
585                 public Rectangle GetBounds (ItemBoundsPortion portion)
586                 {
587                         if (owner == null)
588                                 return Rectangle.Empty;
589                                 
590                         Rectangle rect;
591
592                         switch (portion) {
593                         case ItemBoundsPortion.Icon:
594                                 rect = icon_rect;
595                                 break;
596
597                         case ItemBoundsPortion.Label:
598                                 rect = label_rect;
599                                 break;
600
601                         case ItemBoundsPortion.ItemOnly:
602                                 rect = item_rect;
603                                 break;
604
605                         case ItemBoundsPortion.Entire:
606                                 rect = bounds;
607                                 break;
608
609                         default:
610                                 throw new ArgumentException ("Invalid value for portion.");
611                         }
612
613                         Point item_loc = owner.GetItemLocation (Index);
614                         rect.X += item_loc.X;
615                         rect.Y += item_loc.Y;
616                         return rect;
617                 }
618
619                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
620                 {
621                         // FIXME: TODO
622                 }
623
624                 public virtual void Remove ()
625                 {
626                         if (owner == null)
627                                 return;
628
629                         owner.item_control.CancelEdit (this);
630                         owner.Items.Remove (this);
631                         owner = null;
632                 }
633
634                 public override string ToString ()
635                 {
636                         return string.Format ("ListViewItem: {0}", this.Text);
637                 }
638                 #endregion      // Public Instance Methods
639
640                 #region Protected Methods
641                 protected virtual void Deserialize (SerializationInfo info, StreamingContext context)
642                 {
643                         // FIXME: TODO
644                 }
645
646                 protected virtual void Serialize (SerializationInfo info, StreamingContext context)
647                 {
648                         // FIXME: TODO
649                 }
650                 #endregion      // Protected Methods
651
652                 #region Private Internal Methods
653                 internal Rectangle CheckRectReal {
654                         get {
655                                 Rectangle rect = checkbox_rect;
656                                 Point item_loc = owner.GetItemLocation (Index);
657                                 rect.X += item_loc.X;
658                                 rect.Y += item_loc.Y;
659                                 return rect;
660                         }
661                 }
662                 
663                 Rectangle text_bounds;
664                 internal Rectangle TextBounds {
665                         get {
666                                 Rectangle result = text_bounds;
667                                 Point loc = owner.GetItemLocation (Index);
668                                 result.X += loc.X;
669                                 result.Y += loc.Y;
670                                 return result;
671                         }
672                 }
673
674                 internal ListView Owner {
675                         set {
676                                 if (owner == value)
677                                         return;
678
679                                 owner = value;
680                         }
681                 }
682
683 #if NET_2_0
684                 internal void SetIndex (int index)
685                 {
686                         this.index = index;
687                 }
688 #endif
689
690                 internal void Invalidate ()
691                 {
692                         if (owner == null || owner.item_control == null)
693                                 return;
694
695                         owner.item_control.Invalidate (Bounds);
696                 }
697
698                 internal void Layout ()
699                 {
700                         int item_ht;
701                         Rectangle total;
702                         Size text_size = owner.text_size;
703                         
704                         checkbox_rect = Rectangle.Empty;
705                         if (owner.CheckBoxes)
706                                 checkbox_rect.Size = owner.CheckBoxSize;
707
708                         switch (owner.View) {
709                         case View.Details:
710                                 // LAMESPEC: MSDN says, "In all views except the details
711                                 // view of the ListView, this value specifies the same
712                                 // bounding rectangle as the Entire value." Actually, it
713                                 // returns same bounding rectangles for Item and Entire
714                                 // values in the case of Details view.
715
716                                 icon_rect = label_rect = Rectangle.Empty;
717                                 icon_rect.X = checkbox_rect.Width + 2;
718                                 item_ht = owner.ItemSize.Height;
719
720                                 if (owner.SmallImageList != null)
721                                         icon_rect.Width = owner.SmallImageList.ImageSize.Width;
722
723                                 label_rect.Height = icon_rect.Height = item_ht;
724                                 checkbox_rect.Y = item_ht - checkbox_rect.Height;
725
726                                 label_rect.X = icon_rect.Right + 1;
727
728                                 if (owner.Columns.Count > 0)
729                                         label_rect.Width = owner.Columns [0].Wd - label_rect.X;
730                                 else
731                                         label_rect.Width = text_size.Width;
732
733                                 SizeF text_sz = owner.DeviceContext.MeasureString (Text, Font);
734                                 text_bounds = label_rect;
735                                 text_bounds.Width = (int) text_sz.Width;
736
737                                 item_rect = total = Rectangle.Union
738                                         (Rectangle.Union (checkbox_rect, icon_rect), label_rect);
739                                 bounds.Size = total.Size;
740
741                                 // Take into account the rest of columns. First column
742                                 // is already taken into account above.
743                                 for (int i = 1; i < owner.Columns.Count; i++) {
744                                         item_rect.Width += owner.Columns [i].Wd;
745                                         bounds.Width += owner.Columns [i].Wd;
746                                 }
747                                 break;
748
749                         case View.LargeIcon:
750                                 label_rect = icon_rect = Rectangle.Empty;
751
752                                 SizeF sz = owner.DeviceContext.MeasureString (Text, Font);
753                                 if ((int) sz.Width > text_size.Width) {
754                                         if (Focused) {
755                                                 int text_width = text_size.Width;
756                                                 StringFormat format = new StringFormat ();
757                                                 format.Alignment = StringAlignment.Center;
758                                                 sz = owner.DeviceContext.MeasureString (Text, Font, text_width, format);
759                                                 text_size.Height = (int) sz.Height;
760                                         } else
761                                                 text_size.Height = 2 * (int) sz.Height;
762                                 }
763
764                                 if (owner.LargeImageList != null) {
765                                         icon_rect.Width = owner.LargeImageList.ImageSize.Width;
766                                         icon_rect.Height = owner.LargeImageList.ImageSize.Height;
767                                 }
768
769                                 if (checkbox_rect.Height > icon_rect.Height)
770                                         icon_rect.Y = checkbox_rect.Height - icon_rect.Height;
771                                 else
772                                         checkbox_rect.Y = icon_rect.Height - checkbox_rect.Height;
773
774                                 if (text_size.Width <= icon_rect.Width) {
775                                         icon_rect.X = checkbox_rect.Width + 1;
776                                         label_rect.X = icon_rect.X + (icon_rect.Width - text_size.Width) / 2;
777                                         label_rect.Y = icon_rect.Bottom + 2;
778                                         label_rect.Size = text_size;
779                                 } else {
780                                         int centerX = text_size.Width / 2;
781                                         icon_rect.X = checkbox_rect.Width + 1 + centerX - icon_rect.Width / 2;
782                                         label_rect.X = checkbox_rect.Width + 1;
783                                         label_rect.Y = icon_rect.Bottom + 2;
784                                         label_rect.Size = text_size;
785                                 }
786
787                                 item_rect = Rectangle.Union (icon_rect, label_rect);
788                                 total = Rectangle.Union (item_rect, checkbox_rect);
789                                 bounds.Size = total.Size;
790                                 break;
791
792                         case View.List:
793                         case View.SmallIcon:
794                                 label_rect = icon_rect = Rectangle.Empty;
795                                 icon_rect.X = checkbox_rect.Width + 1;
796                                 item_ht = Math.Max (owner.CheckBoxSize.Height, text_size.Height);
797
798                                 if (owner.SmallImageList != null) {
799                                         item_ht = Math.Max (item_ht, owner.SmallImageList.ImageSize.Height);
800                                         icon_rect.Width = owner.SmallImageList.ImageSize.Width;
801                                         icon_rect.Height = owner.SmallImageList.ImageSize.Height;
802                                 }
803
804                                 checkbox_rect.Y = item_ht - checkbox_rect.Height;
805                                 label_rect.X = icon_rect.Right + 1;
806                                 label_rect.Width = text_size.Width;
807                                 label_rect.Height = icon_rect.Height = item_ht;
808
809                                 item_rect = Rectangle.Union (icon_rect, label_rect);
810                                 total = Rectangle.Union (item_rect, checkbox_rect);
811                                 bounds.Size = total.Size;
812                                 break;
813 #if NET_2_0
814                         case View.Tile:
815                                 label_rect = icon_rect = Rectangle.Empty;
816
817                                 if (owner.LargeImageList != null) {
818                                         icon_rect.Width = owner.LargeImageList.ImageSize.Width;
819                                         icon_rect.Height = owner.LargeImageList.ImageSize.Height;
820                                 }
821
822                                 int separation = 2;
823                                 SizeF tsize = owner.DeviceContext.MeasureString (Text, Font);
824
825                                 // Set initial values for subitem's layout
826                                 int total_height = (int)Math.Ceiling (tsize.Height);
827                                 int max_subitem_width = (int)Math.Ceiling (tsize.Width);
828                                 SubItems [0].bounds.Height = total_height;
829                         
830                                 int count = Math.Min (owner.Columns.Count, SubItems.Count);
831                                 for (int i = 1; i < count; i++) { // Ignore first column and first subitem
832                                         ListViewSubItem sub_item = SubItems [i];
833                                         if (sub_item.Text == null || sub_item.Text.Length == 0)
834                                                 continue;
835
836                                         tsize = owner.DeviceContext.MeasureString (sub_item.Text, sub_item.Font);
837                                 
838                                         int width = (int)Math.Ceiling (tsize.Width);
839                                 
840                                         if (width > max_subitem_width)
841                                                 max_subitem_width = width;
842                                 
843                                         int height = (int)Math.Ceiling (tsize.Height);
844                                         total_height += height + separation;
845                                 
846                                         sub_item.bounds.Height = height;
847                         
848                                 }
849
850                                 label_rect.X = icon_rect.Right + 4;
851                                 label_rect.Y = owner.TileSize.Height / 2 - total_height / 2;
852                                 label_rect.Width = max_subitem_width;
853                                 label_rect.Height = total_height;
854                         
855                                 // Second pass for assigning bounds. This time take first subitem into account.
856                                 int current_y = label_rect.Y;
857                                 for (int j = 0; j < count; j++) {
858                                         ListViewSubItem sub_item = SubItems [j];
859                                         if (sub_item.Text == null || sub_item.Text.Length == 0)
860                                                 continue;
861
862                                         sub_item.SetBounds (label_rect.X, current_y, max_subitem_width, sub_item.bounds.Height);
863                                         current_y += sub_item.Bounds.Height + separation;
864                                 }
865                                 
866                                 item_rect = Rectangle.Union (icon_rect, label_rect);
867                                 bounds.Size = item_rect.Size;
868                                 break;
869 #endif
870                         }
871                         
872                 }
873                 #endregion      // Private Internal Methods
874
875                 #region Subclasses
876
877                 [DefaultProperty ("Text")]
878                 [DesignTimeVisible (false)]
879                 [Serializable]
880                 [ToolboxItem (false)]
881                 [TypeConverter (typeof(ListViewSubItemConverter))]
882                 public class ListViewSubItem
883                 {
884                         private Color back_color;
885                         private Font font;
886                         private Color fore_color;
887                         internal ListViewItem owner;
888                         private string text = string.Empty;
889 #if NET_2_0
890                         private string name = String.Empty;
891                         private object tag;
892                         internal Rectangle bounds;
893 #endif
894                         
895                         #region Public Constructors
896                         public ListViewSubItem ()
897                         {
898                         }
899
900                         public ListViewSubItem (ListViewItem owner, string text)
901                                 : this (owner, text, Color.Empty,
902                                         Color.Empty, null)
903                         {
904                         }
905
906                         public ListViewSubItem (ListViewItem owner, string text, Color foreColor,
907                                                 Color backColor, Font font)
908                         {
909                                 this.owner = owner;
910                                 Text = text;
911                                 this.fore_color = foreColor;
912                                 this.back_color = backColor;
913                                 this.font = font;
914                         }
915                         #endregion // Public Constructors
916
917                         #region Public Instance Properties
918                         public Color BackColor {
919                                 get {
920                                         if (this.back_color != Color.Empty)
921                                                 return this.back_color;
922                                         if (this.owner != null && this.owner.ListView != null)
923                                                 return this.owner.ListView.BackColor;
924                                         return ThemeEngine.Current.ColorWindow;
925                                 }
926                                 set { 
927                                         back_color = value; 
928                                         Invalidate ();
929                                 }
930                         }
931
932 #if NET_2_0
933                         public Rectangle Bounds {
934                                 get {
935                                         Rectangle retval = bounds;
936                                         if (owner != null) {
937                                                 retval.X += owner.Bounds.X;
938                                                 retval.Y += owner.Bounds.Y;
939                                         }
940
941                                         return retval;
942                                 }
943                         }
944 #endif
945
946                         [Localizable (true)]
947                         public Font Font {
948                                 get {
949                                         if (font != null)
950                                                 return font;
951                                         else if (owner != null)
952                                                 return owner.Font;
953                                         return ThemeEngine.Current.DefaultFont;
954                                 }
955                                 set { 
956                                         if (font == value)
957                                                 return;
958                                         font = value; 
959                                         Invalidate ();
960                                     }
961                         }
962
963                         public Color ForeColor {
964                                 get {
965                                         if (this.fore_color != Color.Empty)
966                                                 return this.fore_color;
967                                         if (this.owner != null && this.owner.ListView != null)
968                                                 return this.owner.ListView.ForeColor;
969                                         return ThemeEngine.Current.ColorWindowText;
970                                 }
971                                 set { 
972                                         fore_color = value; 
973                                         Invalidate ();
974                                 }
975                         }
976
977 #if NET_2_0
978                         [Localizable (true)]
979                         public string Name {
980                                 get {
981                                         return name;
982                                 }
983                                 set {
984                                         name = value == null ? String.Empty : value;
985                                 }
986                         }
987
988                         [TypeConverter (typeof (StringConverter))]
989                         [BindableAttribute (true)]
990                         [DefaultValue (null)]
991                         [Localizable (false)]
992                         public object Tag {
993                                 get {
994                                         return tag;
995                                 }
996                                 set {
997                                         tag = value;
998                                 }
999                         }
1000 #endif
1001
1002                         [Localizable (true)]
1003                         public string Text {
1004                                 get { return text; }
1005                                 set { 
1006                                         if(text == value)
1007                                                 return;
1008
1009                                         if(value == null)
1010                                                 text = string.Empty;
1011                                         else
1012                                                 text = value; 
1013
1014                                         Invalidate ();
1015                                     }
1016                         }
1017                         #endregion // Public Instance Properties
1018
1019                         #region Public Methods
1020                         public void ResetStyle ()
1021                         {
1022                                 font = ThemeEngine.Current.DefaultFont;
1023                                 back_color = ThemeEngine.Current.DefaultControlBackColor;
1024                                 fore_color = ThemeEngine.Current.DefaultControlForeColor;
1025                                 Invalidate ();
1026                         }
1027
1028                         public override string ToString ()
1029                         {
1030                                 return string.Format ("ListViewSubItem {{0}}", text);
1031                         }
1032                         #endregion // Public Methods
1033
1034                         
1035                         #region Private Methods
1036                         private void Invalidate ()
1037                         {
1038                                 if (owner == null || owner.owner == null)
1039                                         return;
1040
1041                                 owner.owner.Invalidate ();
1042                         }
1043
1044 #if NET_2_0
1045                         internal int Height {
1046                                 get {
1047                                         return bounds.Height;
1048                                 }
1049                         }
1050
1051                         internal void SetBounds (int x, int y, int width, int height)
1052                         {
1053                                 bounds = new Rectangle (x, y, width, height);
1054                         }
1055 #endif
1056                         #endregion // Private Methods
1057                 }
1058
1059                 public class ListViewSubItemCollection : IList, ICollection, IEnumerable
1060                 {
1061                         private ArrayList list;
1062                         internal ListViewItem owner;
1063
1064                         #region Public Constructors
1065                         public ListViewSubItemCollection (ListViewItem owner)
1066                         {
1067                                 this.owner = owner;
1068                                 this.list = new ArrayList ();
1069                         }
1070                         #endregion // Public Constructors
1071
1072                         #region Public Properties
1073                         [Browsable (false)]
1074                         public int Count {
1075                                 get { return list.Count; }
1076                         }
1077
1078                         public bool IsReadOnly {
1079                                 get { return false; }
1080                         }
1081
1082                         public ListViewSubItem this [int index] {
1083                                 get { return (ListViewSubItem) list [index]; }
1084                                 set { 
1085                                         value.owner = this.owner;
1086                                         list [index] = value;
1087                                 }
1088                         }
1089
1090 #if NET_2_0
1091                         public virtual ListViewSubItem this [string key] {
1092                                 get {
1093                                         int idx = IndexOfKey (key);
1094                                         if (idx == -1)
1095                                                 return null;
1096
1097                                         return (ListViewSubItem) list [idx];
1098                                 }
1099                         }
1100 #endif
1101
1102                         bool ICollection.IsSynchronized {
1103                                 get { return list.IsSynchronized; }
1104                         }
1105
1106                         object ICollection.SyncRoot {
1107                                 get { return list.SyncRoot; }
1108                         }
1109
1110                         bool IList.IsFixedSize {
1111                                 get { return list.IsFixedSize; }
1112                         }
1113
1114                         object IList.this [int index] {
1115                                 get { return this [index]; }
1116                                 set {
1117                                         if (! (value is ListViewSubItem))
1118                                                 throw new ArgumentException ("Not of type ListViewSubItem", "value");
1119                                         this [index] = (ListViewSubItem) value;
1120                                 }
1121                         }
1122                         #endregion // Public Properties
1123
1124                         #region Public Methods
1125                         public ListViewSubItem Add (ListViewSubItem item)
1126                         {
1127                                 item.owner = this.owner;
1128                                 list.Add (item);
1129                                 return item;
1130                         }
1131
1132                         public ListViewSubItem Add (string text)
1133                         {
1134                                 ListViewSubItem item = new ListViewSubItem (this.owner, text);
1135                                 list.Add (item);
1136                                 return item;
1137                         }
1138
1139                         public ListViewSubItem Add (string text, Color foreColor,
1140                                                     Color backColor, Font font)
1141                         {
1142                                 ListViewSubItem item = new ListViewSubItem (this.owner, text,
1143                                                                             foreColor, backColor, font);
1144                                 list.Add (item);
1145                                 return item;
1146                         }
1147
1148                         public void AddRange (ListViewSubItem [] items)
1149                         {
1150                                 if (items == null)
1151                                         throw new ArgumentNullException ("items");
1152
1153                                 foreach (ListViewSubItem item in items) {
1154                                         if (item == null)
1155                                                 continue;
1156                                         this.Add (item);
1157                                 }
1158                         }
1159
1160                         public void AddRange (string [] items)
1161                         {
1162                                 if (items == null)
1163                                         throw new ArgumentNullException ("items");
1164
1165                                 foreach (string item in items) {
1166                                         if (item == null)
1167                                                 continue;
1168                                         this.Add (item);
1169                                 }
1170                         }
1171
1172                         public void AddRange (string [] items, Color foreColor,
1173                                               Color backColor, Font font)
1174                         {
1175                                 if (items == null)
1176                                         throw new ArgumentNullException ("items");
1177
1178                                 foreach (string item in items) {
1179                                         if (item == null)
1180                                                 continue;
1181                                         this.Add (item, foreColor, backColor, font);
1182                                 }
1183                         }
1184
1185                         public void Clear ()
1186                         {
1187                                 list.Clear ();
1188                         }
1189
1190                         public bool Contains (ListViewSubItem item)
1191                         {
1192                                 return list.Contains (item);
1193                         }
1194
1195 #if NET_2_0
1196                         public virtual bool ContainsKey (string key)
1197                         {
1198                                 return IndexOfKey (key) != -1;
1199                         }
1200 #endif
1201
1202                         public IEnumerator GetEnumerator ()
1203                         {
1204                                 return list.GetEnumerator ();
1205                         }
1206
1207                         void ICollection.CopyTo (Array dest, int index)
1208                         {
1209                                 list.CopyTo (dest, index);
1210                         }
1211
1212                         int IList.Add (object item)
1213                         {
1214                                 if (! (item is ListViewSubItem)) {
1215                                         throw new ArgumentException ("Not of type ListViewSubItem", "item");
1216                                 }
1217
1218                                 ListViewSubItem sub_item = (ListViewSubItem) item;
1219                                 sub_item.owner = this.owner;
1220                                 return list.Add (sub_item);
1221                         }
1222
1223                         bool IList.Contains (object subItem)
1224                         {
1225                                 if (! (subItem is ListViewSubItem)) {
1226                                         throw new ArgumentException ("Not of type ListViewSubItem", "subItem");
1227                                 }
1228
1229                                 return this.Contains ((ListViewSubItem) subItem);
1230                         }
1231
1232                         int IList.IndexOf (object subItem)
1233                         {
1234                                 if (! (subItem is ListViewSubItem)) {
1235                                         throw new ArgumentException ("Not of type ListViewSubItem", "subItem");
1236                                 }
1237
1238                                 return this.IndexOf ((ListViewSubItem) subItem);
1239                         }
1240
1241                         void IList.Insert (int index, object item)
1242                         {
1243                                 if (! (item is ListViewSubItem)) {
1244                                         throw new ArgumentException ("Not of type ListViewSubItem", "item");
1245                                 }
1246
1247                                 this.Insert (index, (ListViewSubItem) item);
1248                         }
1249
1250                         void IList.Remove (object item)
1251                         {
1252                                 if (! (item is ListViewSubItem)) {
1253                                         throw new ArgumentException ("Not of type ListViewSubItem", "item");
1254                                 }
1255
1256                                 this.Remove ((ListViewSubItem) item);
1257                         }
1258
1259                         public int IndexOf (ListViewSubItem subItem)
1260                         {
1261                                 return list.IndexOf (subItem);
1262                         }
1263
1264 #if NET_2_0
1265                         public virtual int IndexOfKey (string key)
1266                         {
1267                                 if (key == null || key.Length == 0)
1268                                         return -1;
1269
1270                                 for (int i = 0; i < list.Count; i++) {
1271                                         ListViewSubItem l = (ListViewSubItem) list [i];
1272                                         if (String.Compare (l.Name, key, true) == 0)
1273                                                 return i;
1274                                 }
1275
1276                                 return -1;
1277                         }
1278 #endif
1279
1280                         public void Insert (int index, ListViewSubItem item)
1281                         {
1282                                 item.owner = this.owner;
1283                                 list.Insert (index, item);
1284                         }
1285
1286                         public void Remove (ListViewSubItem item)
1287                         {
1288                                 list.Remove (item);
1289                         }
1290
1291 #if NET_2_0
1292                         public virtual void RemoveByKey (string key)
1293                         {
1294                                 int idx = IndexOfKey (key);
1295                                 if (idx != -1)
1296                                         RemoveAt (idx);
1297                         }
1298 #endif
1299
1300                         public void RemoveAt (int index)
1301                         {
1302                                 list.RemoveAt (index);
1303                         }
1304                         #endregion // Public Methods
1305                 }
1306                 #endregion // Subclasses
1307         }
1308 }