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