8b20cb34d979b2684301ed5f1c335dd5c3225c53
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ListView.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-2005 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Ravindra Kumar (rkumar@novell.com)
24 //      Jordi Mas i Hernandez, jordi@ximian.com
25 //      Mike Kestner (mkestner@novell.com)
26 //
27 // TODO:
28 //   - Feedback for item activation, change in cursor types as mouse moves.
29 //   - LabelEdit
30 //   - Drag and drop
31
32
33 // NOT COMPLETE
34
35
36 using System.Collections;
37 using System.ComponentModel;
38 using System.ComponentModel.Design;
39 using System.Drawing;
40 using System.Runtime.InteropServices;
41 using System.Globalization;
42
43 namespace System.Windows.Forms
44 {
45         [DefaultEvent ("SelectedIndexChanged")]
46         [DefaultProperty ("Items")]
47         [Designer ("System.Windows.Forms.Design.ListViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
48         public class ListView : Control
49         {
50                 private ItemActivation activation = ItemActivation.Standard;
51                 private ListViewAlignment alignment = ListViewAlignment.Top;
52                 private bool allow_column_reorder = false;
53                 private bool auto_arrange = true;
54                 private bool check_boxes = false;
55                 private readonly CheckedIndexCollection checked_indices;
56                 private readonly CheckedListViewItemCollection checked_items;
57                 private readonly ColumnHeaderCollection columns;
58                 internal ListViewItem focused_item;
59                 private bool full_row_select = false;
60                 private bool grid_lines = false;
61                 private ColumnHeaderStyle header_style = ColumnHeaderStyle.Clickable;
62                 private bool hide_selection = true;
63                 private bool hover_selection = false;
64                 private IComparer item_sorter;
65                 private readonly ListViewItemCollection items;
66                 private bool label_edit = false;
67                 private bool label_wrap = true;
68                 private bool multiselect = true;
69                 private bool scrollable = true;
70                 private readonly SelectedIndexCollection selected_indices;
71                 private readonly SelectedListViewItemCollection selected_items;
72                 private SortOrder sort_order = SortOrder.None;
73                 private ImageList state_image_list;
74                 private bool updating = false;
75                 private View view = View.LargeIcon;
76                 private int layout_wd;    // We might draw more than our client area
77                 private int layout_ht;    // therefore we need to have these two.
78                 //private TextBox editor;   // Used for editing an item text
79                 HeaderControl header_control;
80                 internal ItemControl item_control;
81                 internal ScrollBar h_scroll; // used for scrolling horizontally
82                 internal ScrollBar v_scroll; // used for scrolling vertically
83                 internal int h_marker;          // Position markers for scrolling
84                 internal int v_marker;
85                 private int keysearch_tickcnt;
86                 private string keysearch_text;
87                 static private readonly int keysearch_keydelay = 1000;
88                 private int[] reordered_column_indices;
89
90                 // internal variables
91                 internal ImageList large_image_list;
92                 internal ImageList small_image_list;
93                 internal Size text_size = Size.Empty;
94
95                 #region Events
96                 public event LabelEditEventHandler AfterLabelEdit;
97
98                 [Browsable (false)]
99                 [EditorBrowsable (EditorBrowsableState.Never)]
100                 public new event EventHandler BackgroundImageChanged {
101                         add { base.BackgroundImageChanged += value; }
102                         remove { base.BackgroundImageChanged -= value; }
103                 }
104
105                 public event LabelEditEventHandler BeforeLabelEdit;
106                 public event ColumnClickEventHandler ColumnClick;
107                 public event EventHandler ItemActivate;
108                 public event ItemCheckEventHandler ItemCheck;
109                 public event ItemDragEventHandler ItemDrag;
110
111                 [Browsable (false)]
112                 [EditorBrowsable (EditorBrowsableState.Never)]
113                 public new event PaintEventHandler Paint {
114                         add { base.Paint += value; }
115                         remove { base.Paint -= value; }
116                 }
117
118                 public event EventHandler SelectedIndexChanged;
119
120                 [Browsable (false)]
121                 [EditorBrowsable (EditorBrowsableState.Never)]
122                 public new event EventHandler TextChanged {
123                         add { base.TextChanged += value; }
124                         remove { base.TextChanged -= value; }
125                 }
126
127                 #endregion // Events
128
129                 #region Public Constructors
130                 public ListView ()
131                 {
132                         background_color = ThemeEngine.Current.ColorWindow;
133                         items = new ListViewItemCollection (this);
134                         checked_indices = new CheckedIndexCollection (this);
135                         checked_items = new CheckedListViewItemCollection (this);
136                         columns = new ColumnHeaderCollection (this);
137                         foreground_color = SystemColors.WindowText;
138                         selected_indices = new SelectedIndexCollection (this);
139                         selected_items = new SelectedListViewItemCollection (this);
140
141                         border_style = BorderStyle.Fixed3D;
142
143                         header_control = new HeaderControl (this);
144                         header_control.Visible = false;
145                         Controls.AddImplicit (header_control);
146
147                         item_control = new ItemControl (this);
148                         Controls.AddImplicit (item_control);
149
150                         h_scroll = new ImplicitHScrollBar ();
151                         Controls.AddImplicit (this.h_scroll);
152
153                         v_scroll = new ImplicitVScrollBar ();
154                         Controls.AddImplicit (this.v_scroll);
155
156                         h_marker = v_marker = 0;
157                         keysearch_tickcnt = 0;
158
159                         // scroll bars are disabled initially
160                         h_scroll.Visible = false;
161                         h_scroll.ValueChanged += new EventHandler(HorizontalScroller);
162                         v_scroll.Visible = false;
163                         v_scroll.ValueChanged += new EventHandler(VerticalScroller);
164
165                         // event handlers
166                         base.KeyDown += new KeyEventHandler(ListView_KeyDown);
167                         SizeChanged += new EventHandler (ListView_SizeChanged);
168                         GotFocus += new EventHandler (FocusChanged);
169                         LostFocus += new EventHandler (FocusChanged);
170                         MouseWheel += new MouseEventHandler(ListView_MouseWheel);
171
172                         this.SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick
173 #if NET_2_0
174                                 | ControlStyles.UseTextForAccessibility
175 #endif
176                                 , false);
177                 }
178                 #endregion      // Public Constructors
179
180                 #region Private Internal Properties
181                 internal Size CheckBoxSize {
182                         get {
183                                 if (this.check_boxes) {
184                                         if (this.state_image_list != null)
185                                                 return this.state_image_list.ImageSize;
186                                         else
187                                                 return ThemeEngine.Current.ListViewCheckBoxSize;
188                                 }
189                                 return Size.Empty;
190                         }
191                 }
192
193                 #endregion      // Private Internal Properties
194
195                 #region  Protected Properties
196                 protected override CreateParams CreateParams {
197                         get { return base.CreateParams; }
198                 }
199
200                 protected override Size DefaultSize {
201                         get { return ThemeEngine.Current.ListViewDefaultSize; }
202                 }
203                 #endregion      // Protected Properties
204
205                 #region Public Instance Properties
206                 [DefaultValue (ItemActivation.Standard)]
207                 public ItemActivation Activation {
208                         get { return activation; }
209                         set { 
210                                 if (value != ItemActivation.Standard && value != ItemActivation.OneClick && 
211                                         value != ItemActivation.TwoClick) {
212                                         throw new InvalidEnumArgumentException (string.Format
213                                                 ("Enum argument value '{0}' is not valid for Activation", value));
214                                 }
215                                   
216                                 activation = value;
217                         }
218                 }
219
220                 [DefaultValue (ListViewAlignment.Top)]
221                 [Localizable (true)]
222                 public ListViewAlignment Alignment {
223                         get { return alignment; }
224                         set {
225                                 if (value != ListViewAlignment.Default && value != ListViewAlignment.Left && 
226                                         value != ListViewAlignment.SnapToGrid && value != ListViewAlignment.Top) {
227                                         throw new InvalidEnumArgumentException (string.Format 
228                                                 ("Enum argument value '{0}' is not valid for Alignment", value));
229                                 }
230                                 
231                                 if (this.alignment != value) {
232                                         alignment = value;
233                                         // alignment does not matter in Details/List views
234                                         if (this.view == View.LargeIcon ||
235                                             this.View == View.SmallIcon)
236                                                 this.Redraw (true);
237                                 }
238                         }
239                 }
240
241                 [DefaultValue (false)]
242                 public bool AllowColumnReorder {
243                         get { return allow_column_reorder; }
244                         set { allow_column_reorder = value; }
245                 }
246
247                 [DefaultValue (true)]
248                 public bool AutoArrange {
249                         get { return auto_arrange; }
250                         set {
251                                 if (auto_arrange != value) {
252                                         auto_arrange = value;
253                                         // autoarrange does not matter in Details/List views
254                                         if (this.view == View.LargeIcon || this.View == View.SmallIcon)
255                                                 this.Redraw (true);
256                                 }
257                         }
258                 }
259
260                 public override Color BackColor {
261                         get {
262                                 if (background_color.IsEmpty)
263                                         return ThemeEngine.Current.ColorWindow;
264                                 else
265                                         return background_color;
266                         }
267                         set { background_color = value; }
268                 }
269
270                 [Browsable (false)]
271                 [EditorBrowsable (EditorBrowsableState.Never)]
272                 public override Image BackgroundImage {
273                         get { return background_image; }
274                         set {
275                                 if (value == background_image)
276                                         return;
277
278                                 background_image = value;
279                                 OnBackgroundImageChanged (EventArgs.Empty);
280                         }
281                 }
282
283                 [DefaultValue (BorderStyle.Fixed3D)]
284                 [DispId (-504)]
285                 public BorderStyle BorderStyle {
286                         get { return InternalBorderStyle; }
287                         set { InternalBorderStyle = value; }
288                 }
289
290                 [DefaultValue (false)]
291                 public bool CheckBoxes {
292                         get { return check_boxes; }
293                         set {
294                                 if (check_boxes != value) {
295 #if NET_2_0
296                                         if (value && View == View.Tile)
297                                                 throw new NotSupportedException ("CheckBoxes are not"
298                                                         + " supported in Tile view. Choose a different"
299                                                         + " view or set CheckBoxes to false.");
300 #endif
301
302                                         check_boxes = value;
303                                         this.Redraw (true);
304                                 }
305                         }
306                 }
307
308                 [Browsable (false)]
309                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
310                 public CheckedIndexCollection CheckedIndices {
311                         get { return checked_indices; }
312                 }
313
314                 [Browsable (false)]
315                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
316                 public CheckedListViewItemCollection CheckedItems {
317                         get { return checked_items; }
318                 }
319
320                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
321                 [Localizable (true)]
322                 [MergableProperty (false)]
323                 public ColumnHeaderCollection Columns {
324                         get { return columns; }
325                 }
326
327                 [Browsable (false)]
328                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
329                 public ListViewItem FocusedItem {
330                         get {
331                                 return focused_item;
332                         }
333                 }
334
335                 public override Color ForeColor {
336                         get {
337                                 if (foreground_color.IsEmpty)
338                                         return ThemeEngine.Current.ColorWindowText;
339                                 else
340                                         return foreground_color;
341                         }
342                         set { foreground_color = value; }
343                 }
344
345                 [DefaultValue (false)]
346                 public bool FullRowSelect {
347                         get { return full_row_select; }
348                         set { full_row_select = value; }
349                 }
350
351                 [DefaultValue (false)]
352                 public bool GridLines {
353                         get { return grid_lines; }
354                         set {
355                                 if (grid_lines != value) {
356                                         grid_lines = value;
357                                         this.Redraw (false);
358                                 }
359                         }
360                 }
361
362                 [DefaultValue (ColumnHeaderStyle.Clickable)]
363                 public ColumnHeaderStyle HeaderStyle {
364                         get { return header_style; }
365                         set {
366                                 if (header_style == value)
367                                         return;
368
369                                 switch (value) {
370                                 case ColumnHeaderStyle.Clickable:
371                                 case ColumnHeaderStyle.Nonclickable:
372                                 case ColumnHeaderStyle.None:
373                                         break;
374                                 default:
375                                         throw new InvalidEnumArgumentException (string.Format 
376                                                 ("Enum argument value '{0}' is not valid for ColumnHeaderStyle", value));
377                                 }
378                                 
379                                 header_style = value;
380                                 if (view == View.Details)
381                                         Redraw (true);
382                         }
383                 }
384
385                 [DefaultValue (true)]
386                 public bool HideSelection {
387                         get { return hide_selection; }
388                         set {
389                                 if (hide_selection != value) {
390                                         hide_selection = value;
391                                         this.Redraw (false);
392                                 }
393                         }
394                 }
395
396                 [DefaultValue (false)]
397                 public bool HoverSelection {
398                         get { return hover_selection; }
399                         set { hover_selection = value; }
400                 }
401
402                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
403                 [Localizable (true)]
404                 [MergableProperty (false)]              
405                 public ListViewItemCollection Items {
406                         get { return items; }
407                 }
408
409                 [DefaultValue (false)]
410                 public bool LabelEdit {
411                         get { return label_edit; }
412                         set { label_edit = value; }
413                 }
414
415                 [DefaultValue (true)]
416                 [Localizable (true)]
417                 public bool LabelWrap {
418                         get { return label_wrap; }
419                         set {
420                                 if (label_wrap != value) {
421                                         label_wrap = value;
422                                         this.Redraw (true);
423                                 }
424                         }
425                 }
426
427                 [DefaultValue (null)]
428                 public ImageList LargeImageList {
429                         get { return large_image_list; }
430                         set {
431                                 large_image_list = value;
432                                 this.Redraw (true);
433                         }
434                 }
435
436                 [Browsable (false)]
437                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
438                 public IComparer ListViewItemSorter {
439                         get {
440                                 if (View != View.SmallIcon && View != View.LargeIcon && item_sorter is ItemComparer)
441                                         return null;
442                                 return item_sorter;
443                         }
444                         set {
445                                 if (item_sorter != value) {
446                                         item_sorter = value;
447                                         Sort ();
448                                 }
449                         }
450                 }
451
452                 [DefaultValue (true)]
453                 public bool MultiSelect {
454                         get { return multiselect; }
455                         set { multiselect = value; }
456                 }
457
458                 [DefaultValue (true)]
459                 public bool Scrollable {
460                         get { return scrollable; }
461                         set {
462                                 if (scrollable != value) {
463                                         scrollable = value;
464                                         this.Redraw (true);
465                                 }
466                         }
467                 }
468
469                 [Browsable (false)]
470                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
471                 public SelectedIndexCollection SelectedIndices {
472                         get { return selected_indices; }
473                 }
474
475                 [Browsable (false)]
476                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
477                 public SelectedListViewItemCollection SelectedItems {
478                         get { return selected_items; }
479                 }
480
481 #if NET_2_0
482                 [MonoTODO("Implement")]
483                 public bool ShowGroups {
484                         get {
485                                 return false;
486                         }
487
488                         set {
489                         }
490                 }
491 #endif
492
493                 [DefaultValue (null)]
494                 public ImageList SmallImageList {
495                         get { return small_image_list; }
496                         set {
497                                 small_image_list = value;
498                                 this.Redraw (true);
499                         }
500                 }
501
502                 [DefaultValue (SortOrder.None)]
503                 public SortOrder Sorting {
504                         get { return sort_order; }
505                         set { 
506                                 if (!Enum.IsDefined (typeof (SortOrder), value)) {
507                                         throw new InvalidEnumArgumentException ("value", (int) value,
508                                                 typeof (SortOrder));
509                                 }
510                                 
511                                 if (sort_order == value)
512                                         return;
513
514                                 sort_order = value;
515
516                                 if (value == SortOrder.None) {
517                                         if (item_sorter != null) {
518                                                 // ListViewItemSorter should never be reset for SmallIcon
519                                                 // and LargeIcon view
520                                                 if (View != View.SmallIcon && View != View.LargeIcon)
521 #if NET_2_0
522                                                         item_sorter = null;
523 #else
524                                                         // in .NET 1.1, only internal IComparer would be
525                                                         // set to null
526                                                         if (item_sorter is ItemComparer)
527                                                                 item_sorter = null;
528 #endif
529                                         }
530                                         this.Redraw (false);
531                                 } else {
532                                         if (item_sorter == null)
533                                                 item_sorter = new ItemComparer (value);
534                                         if (item_sorter is ItemComparer) {
535 #if NET_2_0
536                                                 item_sorter = new ItemComparer (value);
537 #else
538                                                 // in .NET 1.1, the sort order is not updated for
539                                                 // SmallIcon and LargeIcon views if no custom IComparer
540                                                 // is set
541                                                 if (View != View.SmallIcon && View != View.LargeIcon)
542                                                         item_sorter = new ItemComparer (value);
543 #endif
544                                         }
545                                         Sort ();
546                                 }
547                         }
548                 }
549
550                 [DefaultValue (null)]
551                 public ImageList StateImageList {
552                         get { return state_image_list; }
553                         set {
554                                 state_image_list = value;
555                                 this.Redraw (true);
556                         }
557                 }
558
559                 [Bindable (false)]
560                 [Browsable (false)]
561                 [EditorBrowsable (EditorBrowsableState.Never)]
562                 public override string Text {
563                         get { return text; } 
564                         set {
565                                 if (value == text)
566                                         return;
567
568                                 text = value;
569                                 this.Redraw (true);
570
571                                 OnTextChanged (EventArgs.Empty);
572                         }
573                 }
574
575                 [Browsable (false)]
576                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
577                 public ListViewItem TopItem {
578                         get {
579                                 // there is no item
580                                 if (this.items.Count == 0)
581                                         return null;
582                                 // if contents are not scrolled
583                                 // it is the first item
584                                 else if (h_marker == 0 && v_marker == 0)
585                                         return this.items [0];
586                                 // do a hit test for the scrolled position
587                                 else {
588                                         foreach (ListViewItem item in this.items) {
589                                                 if (item.Bounds.X >= 0 && item.Bounds.Y >= 0)
590                                                         return item;
591                                         }
592                                         return null;
593                                 }
594                         }
595                 }
596
597 #if NET_2_0
598                 [MonoTODO("Implement")]
599                 public bool UseCompatibleStateImageBehavior {
600                         get {
601                                 return false;
602                         }
603
604                         set {
605                         }
606                 }
607 #endif
608
609                 [DefaultValue (View.LargeIcon)]
610                 public View View {
611                         get { return view; }
612                         set { 
613                                 if (!Enum.IsDefined (typeof (View), value))
614                                         throw new InvalidEnumArgumentException ("value", (int) value,
615                                                 typeof (View));
616
617                                 if (view != value) {
618 #if NET_2_0
619                                         if (CheckBoxes && value == View.Tile)
620                                                 throw new NotSupportedException ("CheckBoxes are not"
621                                                         + " supported in Tile view. Choose a different"
622                                                         + " view or set CheckBoxes to false.");
623 #endif
624
625                                         h_scroll.Value = v_scroll.Value = 0;
626                                         view = value; 
627                                         Redraw (true);
628                                 }
629                         }
630                 }
631                 #endregion      // Public Instance Properties
632
633                 #region Internal Methods Properties
634                 
635                 internal int FirstVisibleIndex {
636                         get {
637                                 // there is no item
638                                 if (this.items.Count == 0)
639                                         return 0;
640                                                                         
641                                 if (h_marker == 0 && v_marker == 0)
642                                         return 0;                                       
643                                 
644                                 foreach (ListViewItem item in this.items) {
645                                         if (item.Bounds.Right >= 0 && item.Bounds.Bottom >= 0)
646                                                 return item.Index;
647                                 }
648                                 return 0;
649
650                         }
651                 }
652
653                 
654                 internal int LastVisibleIndex {                 
655                         get {                                                   
656                                 for (int i = FirstVisibleIndex; i < Items.Count; i++) {
657                                         if (View == View.List || Alignment == ListViewAlignment.Left) {
658                                                 if (Items[i].Bounds.X > ClientRectangle.Right)
659                                                         return i - 1;                                   
660                                         } else {
661                                                 if (Items[i].Bounds.Y > ClientRectangle.Bottom)
662                                                         return i - 1;                                   
663                                         }
664                                 }
665                                 
666                                 return Items.Count - 1;
667                         }
668                 }
669                 
670                 internal void OnSelectedIndexChanged ()
671                 {
672                         if (IsHandleCreated)
673                                 OnSelectedIndexChanged (EventArgs.Empty);
674                 }
675
676                 internal int TotalWidth {
677                         get { return Math.Max (this.Width, this.layout_wd); }
678                 }
679
680                 internal int TotalHeight {
681                         get { return Math.Max (this.Height, this.layout_ht); }
682                 }
683
684                 internal void Redraw (bool recalculate)
685                 {
686                         // Avoid calculations when control is being updated
687                         if (this.updating)
688                                 return;
689
690                         if (recalculate)
691                                 CalculateListView (this.alignment);
692
693                         Refresh ();
694                 }
695
696                 internal Size GetChildColumnSize (int index)
697                 {
698                         Size ret_size = Size.Empty;
699                         ColumnHeader col = this.columns [index];
700
701                         if (col.Width == -2) { // autosize = max(items, columnheader)
702                                 Size size = Size.Ceiling (this.DeviceContext.MeasureString
703                                                           (col.Text, this.Font));
704                                 ret_size = BiggestItem (index);
705                                 if (size.Width > ret_size.Width)
706                                         ret_size = size;
707                         }
708                         else { // -1 and all the values < -2 are put under one category
709                                 ret_size = BiggestItem (index);
710                                 // fall back to empty columns' width if no subitem is available for a column
711                                 if (ret_size.IsEmpty) {
712                                         ret_size.Width = ThemeEngine.Current.ListViewEmptyColumnWidth;
713                                         if (col.Text.Length > 0)
714                                                 ret_size.Height = Size.Ceiling (this.DeviceContext.MeasureString
715                                                                                 (col.Text, this.Font)).Height;
716                                         else
717                                                 ret_size.Height = this.Font.Height;
718                                 }
719                         }
720
721                         // adjust the size for icon and checkbox for 0th column
722                         if (index == 0) {
723                                 ret_size.Width += (this.CheckBoxSize.Width + 4);
724                                 if (this.small_image_list != null)
725                                         ret_size.Width += this.small_image_list.ImageSize.Width;
726                         }
727                         return ret_size;
728                 }
729
730                 // Returns the size of biggest item text in a column.
731                 private Size BiggestItem (int col)
732                 {
733                         Size temp = Size.Empty;
734                         Size ret_size = Size.Empty;
735
736                         // 0th column holds the item text, we check the size of
737                         // the various subitems falling in that column and get
738                         // the biggest one's size.
739                         foreach (ListViewItem item in items) {
740                                 if (col >= item.SubItems.Count)
741                                         continue;
742
743                                 temp = Size.Ceiling (this.DeviceContext.MeasureString
744                                                      (item.SubItems [col].Text, this.Font));
745                                 if (temp.Width > ret_size.Width)
746                                         ret_size = temp;
747                         }
748
749                         // adjustment for space
750                         if (!ret_size.IsEmpty)
751                                 ret_size.Width += 4;
752
753                         return ret_size;
754                 }
755
756                 const int max_wrap_padding = 38;
757
758                 // Sets the size of the biggest item text as per the view
759                 private void CalcTextSize ()
760                 {                       
761                         // clear the old value
762                         text_size = Size.Empty;
763
764                         if (items.Count == 0)
765                                 return;
766
767                         text_size = BiggestItem (0);
768
769                         if (view == View.LargeIcon && this.label_wrap) {
770                                 Size temp = Size.Empty;
771                                 if (this.check_boxes)
772                                         temp.Width += 2 * this.CheckBoxSize.Width;
773                                 int icon_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
774                                 temp.Width += icon_w + max_wrap_padding;
775                                 // wrapping is done for two lines only
776                                 if (text_size.Width > temp.Width) {
777                                         text_size.Width = temp.Width;
778                                         text_size.Height *= 2;
779                                 }
780                         }
781                         else if (view == View.List) {
782                                 // in list view max text shown in determined by the
783                                 // control width, even if scolling is enabled.
784                                 int max_wd = this.Width - (this.CheckBoxSize.Width - 2);
785                                 if (this.small_image_list != null)
786                                         max_wd -= this.small_image_list.ImageSize.Width;
787
788                                 if (text_size.Width > max_wd)
789                                         text_size.Width = max_wd;
790                         }
791
792                         // we do the default settings, if we have got 0's
793                         if (text_size.Height <= 0)
794                                 text_size.Height = this.Font.Height;
795                         if (text_size.Width <= 0)
796                                 text_size.Width = this.Width;
797
798                         // little adjustment
799                         text_size.Width += 4;
800                         text_size.Height += 2;
801                 }
802
803                 private void Scroll (ScrollBar scrollbar, int delta)
804                 {
805                         if (delta == 0 || !scrollbar.Visible)
806                                 return;
807
808                         int max;
809                         if (scrollbar == h_scroll)
810                                 max = h_scroll.Maximum - item_control.Width;
811                         else
812                                 max = v_scroll.Maximum - item_control.Height;
813
814                         int val = scrollbar.Value + delta;
815                         if (val > max)
816                                 val = max;
817                         else if (val < scrollbar.Minimum)
818                                 val = scrollbar.Minimum;
819                         scrollbar.Value = val;
820                 }
821
822                 private void CalculateScrollBars ()
823                 {
824                         Rectangle client_area = ClientRectangle;
825                         
826                         if (!this.scrollable || this.items.Count <= 0) {
827                                 h_scroll.Visible = false;
828                                 v_scroll.Visible = false;
829                                 item_control.Location = new Point (0, header_control.Height);
830                                 item_control.Height = ClientRectangle.Width - header_control.Height;
831                                 item_control.Width = ClientRectangle.Width;
832                                 header_control.Width = ClientRectangle.Width;
833                                 return;
834                         }
835
836                         // Don't calculate if the view is not displayable
837                         if (client_area.Height < 0 || client_area.Width < 0)
838                                 return;
839
840                         // making a scroll bar visible might make
841                         // other scroll bar visible                     
842                         if (layout_wd > client_area.Right) {
843                                 h_scroll.Visible = true;
844                                 if ((layout_ht + h_scroll.Height) > client_area.Bottom)
845                                         v_scroll.Visible = true;                                        
846                                 else
847                                         v_scroll.Visible = false;
848                         } else if (layout_ht > client_area.Bottom) {                            
849                                 v_scroll.Visible = true;
850                                 if ((layout_wd + v_scroll.Width) > client_area.Right)
851                                         h_scroll.Visible = true;
852                                 else
853                                         h_scroll.Visible = false;
854                         } else {
855                                 h_scroll.Visible = false;
856                                 v_scroll.Visible = false;
857                         }                       
858
859                         item_control.Height = ClientRectangle.Height - header_control.Height;
860
861                         if (h_scroll.is_visible) {
862                                 h_scroll.Location = new Point (client_area.X, client_area.Bottom - h_scroll.Height);
863                                 h_scroll.Minimum = 0;
864
865                                 // if v_scroll is visible, adjust the maximum of the
866                                 // h_scroll to account for the width of v_scroll
867                                 if (v_scroll.Visible) {
868                                         h_scroll.Maximum = layout_wd + v_scroll.Width;
869                                         h_scroll.Width = client_area.Width - v_scroll.Width;
870                                 }
871                                 else {
872                                         h_scroll.Maximum = layout_wd;
873                                         h_scroll.Width = client_area.Width;
874                                 }
875    
876                                 h_scroll.LargeChange = client_area.Width;
877                                 h_scroll.SmallChange = Font.Height;
878                                 item_control.Height -= h_scroll.Height;
879                         }
880
881                         if (header_control.is_visible)
882                                 header_control.Width = ClientRectangle.Width;
883                         item_control.Width = ClientRectangle.Width;
884
885                         if (v_scroll.is_visible) {
886                                 v_scroll.Location = new Point (client_area.Right - v_scroll.Width, client_area.Y);
887                                 v_scroll.Minimum = 0;
888
889                                 // if h_scroll is visible, adjust the maximum of the
890                                 // v_scroll to account for the height of h_scroll
891                                 if (h_scroll.Visible) {
892                                         v_scroll.Maximum = layout_ht + h_scroll.Height;
893                                         v_scroll.Height = client_area.Height; // - h_scroll.Height already done 
894                                 } else {
895                                         v_scroll.Maximum = layout_ht;
896                                         v_scroll.Height = client_area.Height;
897                                 }
898
899                                 v_scroll.LargeChange = client_area.Height;
900                                 v_scroll.SmallChange = Font.Height;
901                                 if (header_control.Visible)
902                                         header_control.Width -= v_scroll.Width;
903                                 item_control.Width -= v_scroll.Width;
904                         }
905                 }
906                 
907                 ColumnHeader GetReorderedColumn (int index)
908                 {
909                         if (reordered_column_indices == null)
910                                 return Columns [index];
911                         else
912                                 return Columns [reordered_column_indices [index]];
913                 }
914
915                 void ReorderColumn (ColumnHeader col, int index)
916                 {
917                         if (reordered_column_indices == null) {
918                                 reordered_column_indices = new int [Columns.Count];
919                                 for (int i = 0; i < Columns.Count; i++)
920                                         reordered_column_indices [i] = i;
921                         }
922
923                         if (reordered_column_indices [index] == col.Index)
924                                 return;
925
926                         int[] curr = reordered_column_indices;
927                         int[] result = new int [Columns.Count];
928                         int curr_idx = 0;
929                         for (int i = 0; i < Columns.Count; i++) {
930                                 if (curr_idx < Columns.Count && curr [curr_idx] == col.Index)
931                                         curr_idx++;
932
933                                 if (i == index)
934                                         result [i] = col.Index;
935                                 else
936                                         result [i] = curr [curr_idx++];
937                         }
938
939                         reordered_column_indices = result;
940                         LayoutDetails ();
941                         header_control.Invalidate ();
942                         item_control.Invalidate ();
943                 }
944
945                 Size LargeIconItemSize {
946                         get {
947                                 int image_w = LargeImageList == null ? 12 : LargeImageList.ImageSize.Width;
948                                 int image_h = LargeImageList == null ? 2 : LargeImageList.ImageSize.Height;
949                                 int w = CheckBoxSize.Width + 2 + Math.Max (text_size.Width, image_w);
950                                 int h = text_size.Height + 2 + Math.Max (CheckBoxSize.Height, image_h);
951                                 return new Size (w, h);
952                         }
953                 }
954
955                 Size SmallIconItemSize {
956                         get {
957                                 int image_w = SmallImageList == null ? 0 : SmallImageList.ImageSize.Width;
958                                 int image_h = SmallImageList == null ? 0 : SmallImageList.ImageSize.Height;
959                                 int w = text_size.Width + 2 + CheckBoxSize.Width + image_w;
960                                 int h = Math.Max (text_size.Height, Math.Max (CheckBoxSize.Height, image_h));
961                                 return new Size (w, h);
962                         }
963                 }
964
965                 int rows;
966                 int cols;
967                 ListViewItem[,] item_matrix;
968
969                 void LayoutIcons (bool large_icons, bool left_aligned, int x_spacing, int y_spacing)
970                 {
971                         header_control.Visible = false;
972                         header_control.Size = Size.Empty;
973                         item_control.Visible = true;
974                         item_control.Location = Point.Empty;
975
976                         if (items.Count == 0)
977                                 return;
978
979                         Size sz = large_icons ? LargeIconItemSize : SmallIconItemSize;
980
981                         Rectangle area = ClientRectangle;
982
983                         if (left_aligned) {
984                                 rows = (int) Math.Floor ((double)(area.Height - h_scroll.Height + y_spacing) / (double)(sz.Height + y_spacing));
985                                 if (rows <= 0)
986                                         rows = 1;
987                                 cols = (int) Math.Ceiling ((double)items.Count / (double)rows);
988                         } else {
989                                 cols = (int) Math.Floor ((double)(area.Width - v_scroll.Width + x_spacing) / (double)(sz.Width + x_spacing));
990                                 if (cols <= 0)
991                                         cols = 1;
992                                 rows = (int) Math.Ceiling ((double)items.Count / (double)cols);
993                         }
994
995                         layout_ht = rows * (sz.Height + y_spacing) - y_spacing;
996                         layout_wd = cols * (sz.Width + x_spacing) - x_spacing;
997                         item_matrix = new ListViewItem [rows, cols];
998                         int row = 0;
999                         int col = 0;
1000                         foreach (ListViewItem item in items) {
1001                                 int x = col * (sz.Width + x_spacing);
1002                                 int y = row * (sz.Height + y_spacing);
1003                                 item.Location = new Point (x, y);
1004                                 item.Layout ();
1005                                 item.row = row;
1006                                 item.col = col;
1007                                 item_matrix [row, col] = item;
1008                                 if (left_aligned) {
1009                                         if (++row == rows) {
1010                                                 row = 0;
1011                                                 col++;
1012                                         }
1013                                 } else {
1014                                         if (++col == cols) {
1015                                                 col = 0;
1016                                                 row++;
1017                                         }
1018                                 }
1019                         }
1020
1021                         item_control.Size = new Size (layout_wd, layout_ht);
1022                 }
1023
1024                 void LayoutHeader ()
1025                 {
1026                         int x = 0;
1027                         for (int i = 0; i < Columns.Count; i++) {
1028                                 ColumnHeader col = GetReorderedColumn (i);
1029                                 col.X = x;
1030                                 col.Y = 0;
1031                                 col.CalcColumnHeader ();
1032                                 x += col.Wd;
1033                         }
1034
1035                         if (x < ClientRectangle.Width)
1036                                 x = ClientRectangle.Width;
1037
1038                         if (header_style == ColumnHeaderStyle.None) {
1039                                 header_control.Visible = false;
1040                                 header_control.Size = Size.Empty;
1041                         } else {
1042                                 header_control.Width = x;
1043                                 header_control.Height = columns [0].Ht;
1044                                 header_control.Visible = true;
1045                         }
1046                 }
1047
1048                 void LayoutDetails ()
1049                 {
1050                         if (columns.Count == 0) {
1051                                 header_control.Visible = false;
1052                                 item_control.Visible = false;
1053                                 return;
1054                         }
1055
1056                         LayoutHeader ();
1057
1058                         item_control.Visible = true;
1059                         item_control.Location = new Point (0, header_control.Height);
1060
1061                         int y = 0; 
1062                         if (items.Count > 0) {
1063                                 foreach (ListViewItem item in items) {
1064                                         item.Layout ();
1065                                         item.Location = new Point (0, y);
1066                                         y += item.Bounds.Height + 2;
1067                                 }
1068
1069                                 // some space for bottom gridline
1070                                 if (grid_lines)
1071                                         y += 2;
1072                         }
1073
1074                         layout_wd = Math.Max (header_control.Width, item_control.Width);
1075                         layout_ht = y + header_control.Height;
1076                 }
1077
1078                 private void CalculateListView (ListViewAlignment align)
1079                 {
1080                         CalcTextSize ();
1081
1082                         switch (view) {
1083                         case View.Details:
1084                                 LayoutDetails ();
1085                                 break;
1086
1087                         case View.SmallIcon:
1088                                 LayoutIcons (false, alignment == ListViewAlignment.Left, 4, 2);
1089                                 break;
1090
1091                         case View.LargeIcon:
1092                                 LayoutIcons (true, alignment == ListViewAlignment.Left,
1093                                              ThemeEngine.Current.ListViewHorizontalSpacing,
1094                                              ThemeEngine.Current.ListViewVerticalSpacing);
1095                                 break;
1096
1097                         case View.List:
1098                                 LayoutIcons (false, true, 4, 2);
1099                                 break;
1100                         }
1101
1102                         CalculateScrollBars ();
1103                 }
1104
1105                 bool HaveModKeys {
1106                         get {
1107                                 return (XplatUI.State.ModifierKeys & (Keys.Control | Keys.Shift)) != 0;
1108                         }
1109                 }
1110
1111                 private bool KeySearchString (KeyEventArgs ke)
1112                 {
1113                         int current_tickcnt = Environment.TickCount;
1114                         if (keysearch_tickcnt > 0 && current_tickcnt - keysearch_tickcnt > keysearch_keydelay) {
1115                                 keysearch_text = string.Empty;
1116                         }
1117                         
1118                         keysearch_text += (char) ke.KeyData;
1119                         keysearch_tickcnt = current_tickcnt;
1120
1121                         int start = FocusedItem == null ? 0 : FocusedItem.Index;
1122                         int i = start;
1123                         while (true) {
1124                                 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (Items[i].Text, keysearch_text,
1125                                         CompareOptions.IgnoreCase)) {
1126                                         SetFocusedItem (Items [i]);
1127                                         items [i].Selected = true;
1128                                         EnsureVisible (i);
1129                                         break;
1130                                 }
1131                                 i = (i + 1  < Items.Count) ? i+1 : 0;
1132
1133                                 if (i == start)
1134                                         break;
1135                         }
1136                         return true;
1137                 }
1138
1139                 int GetAdjustedIndex (Keys key)
1140                 {
1141                         int result = -1;
1142
1143                         if (View == View.Details) {
1144                                 if (key == Keys.Up)
1145                                         result = FocusedItem.Index - 1;
1146                                 else if (key == Keys.Down) {
1147                                         result = FocusedItem.Index + 1;
1148                                         if (result == items.Count)
1149                                                 result = -1;
1150                                 }
1151                                 return result;
1152                         }
1153
1154                         int row = FocusedItem.row;
1155                         int col = FocusedItem.col;
1156
1157                         switch (key) {
1158                         case Keys.Left:
1159                                 if (col == 0)
1160                                         return -1;
1161                                 return item_matrix [row, col - 1].Index;
1162
1163                         case Keys.Right:
1164                                 if (col == (cols - 1))
1165                                         return -1;
1166                                 while (item_matrix [row, col + 1] == null)
1167                                        row--;   
1168                                 return item_matrix [row, col + 1].Index;
1169
1170                         case Keys.Up:
1171                                 if (row == 0)
1172                                         return -1;
1173                                 return item_matrix [row - 1, col].Index;
1174
1175                         case Keys.Down:
1176                                 if (row == (rows - 1) || row == Items.Count - 1)
1177                                         return -1;
1178                                 while (item_matrix [row + 1, col] == null)
1179                                        col--;   
1180                                 return item_matrix [row + 1, col].Index;
1181
1182                         default:
1183                                 return -1;
1184                         }
1185                 }
1186
1187                 ListViewItem selection_start;
1188
1189                 private bool SelectItems (ArrayList sel_items)
1190                 {
1191                         bool changed = false;
1192                         ArrayList curr_items = SelectedItems.List;
1193                         foreach (ListViewItem item in curr_items)
1194                                 if (!sel_items.Contains (item)) {
1195                                         item.Selected = false;
1196                                         changed = true;
1197                                 }
1198                         foreach (ListViewItem item in sel_items)
1199                                 if (!item.Selected) {
1200                                         item.Selected = true;
1201                                         changed = true;
1202                                 }
1203                         return changed;
1204                 }
1205
1206                 private void UpdateMultiSelection (int index)
1207                 {
1208                         bool shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1209                         bool ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1210                         ListViewItem item = items [index];
1211
1212                         if (shift_pressed && selection_start != null) {
1213                                 ArrayList list = new ArrayList ();
1214                                 int start = Math.Min (selection_start.Index, index);
1215                                 int end = Math.Max (selection_start.Index, index);
1216                                 if (View == View.Details) {
1217                                         for (int i = start; i <= end; i++)
1218                                                 list.Add (items [i]);
1219                                 } else {
1220                                         int left = Math.Min (items [start].col, items [end].col);
1221                                         int right = Math.Max (items [start].col, items [end].col);
1222                                         int top = Math.Min (items [start].row, items [end].row);
1223                                         int bottom = Math.Max (items [start].row, items [end].row);
1224                                         foreach (ListViewItem curr in items)
1225                                                 if (curr.row >= top && curr.row <= bottom && 
1226                                                     curr.col >= left && curr.col <= right)
1227                                                         list.Add (curr);
1228                                 }
1229                                 if (SelectItems (list))
1230                                         OnSelectedIndexChanged (EventArgs.Empty);
1231                         } else  if (ctrl_pressed) {
1232                                 item.Selected = !item.Selected;
1233                                 selection_start = item;
1234                                 OnSelectedIndexChanged (EventArgs.Empty);
1235                         } else {
1236                                 SelectedItems.Clear ();
1237                                 item.Selected = true;
1238                                 selection_start = item;
1239                                 OnSelectedIndexChanged (EventArgs.Empty);
1240                         }
1241                 }
1242
1243                 internal override bool InternalPreProcessMessage (ref Message msg)
1244                 {
1245                         if (msg.Msg == (int)Msg.WM_KEYDOWN) {
1246                                 Keys key_data = (Keys)msg.WParam.ToInt32();
1247                                 if (HandleNavKeys (key_data))
1248                                         return true;
1249                         } 
1250                         return base.InternalPreProcessMessage (ref msg);
1251                 }
1252
1253                 bool HandleNavKeys (Keys key_data)
1254                 {
1255                         if (Items.Count == 0 || !item_control.Visible)
1256                                 return false;
1257
1258                         if (FocusedItem == null)
1259                                 SetFocusedItem (Items [0]);
1260
1261                         switch (key_data) {
1262                         case Keys.End:
1263                                 SelectIndex (Items.Count - 1);
1264                                 break;
1265
1266                         case Keys.Home:                 
1267                                 SelectIndex (0);
1268                                 break;
1269
1270                         case Keys.Left:
1271                         case Keys.Right:
1272                         case Keys.Up:                           
1273                         case Keys.Down:
1274                                 SelectIndex (GetAdjustedIndex (key_data));
1275                                 break;
1276
1277                         default:
1278                                 return false;
1279                         }
1280
1281                         return true;
1282                 }
1283
1284                 void SelectIndex (int index)
1285                 {
1286                         if (index == -1)
1287                                 return;
1288
1289                         if (MultiSelect)
1290                                 UpdateMultiSelection (index);
1291                         else if (!items [index].Selected) {
1292                                 items [index].Selected = true;
1293                                 OnSelectedIndexChanged (EventArgs.Empty);
1294                         }
1295
1296                         SetFocusedItem (items [index]);                         
1297                         EnsureVisible (index);
1298                 }
1299
1300                 private void ListView_KeyDown (object sender, KeyEventArgs ke)
1301                 {                       
1302                         if (ke.Handled || Items.Count == 0 || !item_control.Visible)
1303                                 return;
1304
1305                         ke.Handled = KeySearchString (ke);
1306                 }
1307
1308                 internal class ItemControl : Control {
1309
1310                         ListView owner;
1311                         ListViewItem clicked_item;
1312                         ListViewItem last_clicked_item;
1313                         bool hover_processed = false;
1314                         bool checking = false;
1315                         
1316                         ListViewLabelEditTextBox edit_text_box;
1317                         internal ListViewItem edit_item;
1318                         LabelEditEventArgs edit_args;
1319
1320                         public ItemControl (ListView owner)
1321                         {
1322                                 this.owner = owner;
1323                                 DoubleClick += new EventHandler(ItemsDoubleClick);
1324                                 MouseDown += new MouseEventHandler(ItemsMouseDown);
1325                                 MouseMove += new MouseEventHandler(ItemsMouseMove);
1326                                 MouseHover += new EventHandler(ItemsMouseHover);
1327                                 MouseUp += new MouseEventHandler(ItemsMouseUp);
1328                         }
1329
1330                         void ItemsDoubleClick (object sender, EventArgs e)
1331                         {
1332                                 if (owner.activation == ItemActivation.Standard && owner.ItemActivate != null)
1333                                         owner.ItemActivate (this, e);
1334                         }
1335
1336                         enum BoxSelect {
1337                                 None,
1338                                 Normal,
1339                                 Shift,
1340                                 Control
1341                         }
1342
1343                         BoxSelect box_select_mode = BoxSelect.None;
1344                         ArrayList prev_selection;
1345                         Point box_select_start;
1346
1347                         Rectangle box_select_rect;
1348                         internal Rectangle BoxSelectRectangle {
1349                                 get { return box_select_rect; }
1350                                 set {
1351                                         if (box_select_rect == value)
1352                                                 return;
1353
1354                                         InvalidateBoxSelectRect ();
1355                                         box_select_rect = value;
1356                                         InvalidateBoxSelectRect ();
1357                                 }
1358                         }
1359
1360                         void InvalidateBoxSelectRect ()
1361                         {
1362                                 if (BoxSelectRectangle.Size.IsEmpty)
1363                                         return;
1364
1365                                 Rectangle edge = BoxSelectRectangle;
1366                                 edge.X -= 1;
1367                                 edge.Y -= 1;
1368                                 edge.Width += 2;
1369                                 edge.Height = 2;
1370                                 Invalidate (edge);
1371                                 edge.Y = BoxSelectRectangle.Bottom - 1;
1372                                 Invalidate (edge);
1373                                 edge.Y = BoxSelectRectangle.Y - 1;
1374                                 edge.Width = 2;
1375                                 edge.Height = BoxSelectRectangle.Height + 2;
1376                                 Invalidate (edge);
1377                                 edge.X = BoxSelectRectangle.Right - 1;
1378                                 Invalidate (edge);
1379                         }
1380
1381                         private Rectangle CalculateBoxSelectRectangle (Point pt)
1382                         {
1383                                 int left = Math.Min (box_select_start.X, pt.X);
1384                                 int right = Math.Max (box_select_start.X, pt.X);
1385                                 int top = Math.Min (box_select_start.Y, pt.Y);
1386                                 int bottom = Math.Max (box_select_start.Y, pt.Y);
1387                                 return Rectangle.FromLTRB (left, top, right, bottom);
1388                         }
1389
1390                         ArrayList BoxSelectedItems {
1391                                 get {
1392                                         ArrayList result = new ArrayList ();
1393                                         foreach (ListViewItem item in owner.Items) {
1394                                                 Rectangle r = item.Bounds;
1395                                                 r.X += r.Width / 4;
1396                                                 r.Y += r.Height / 4;
1397                                                 r.Width /= 2;
1398                                                 r.Height /= 2;
1399                                                 if (BoxSelectRectangle.IntersectsWith (r))
1400                                                         result.Add (item);
1401                                         }
1402                                         return result;
1403                                 }
1404                         }
1405
1406                         private bool PerformBoxSelection (Point pt)
1407                         {
1408                                 if (box_select_mode == BoxSelect.None)
1409                                         return false;
1410
1411                                 BoxSelectRectangle = CalculateBoxSelectRectangle (pt);
1412                                 
1413                                 ArrayList box_items = BoxSelectedItems;
1414
1415                                 ArrayList items;
1416
1417                                 switch (box_select_mode) {
1418
1419                                 case BoxSelect.Normal:
1420                                         items = box_items;
1421                                         break;
1422
1423                                 case BoxSelect.Control:
1424                                         items = new ArrayList ();
1425                                         foreach (ListViewItem item in prev_selection)
1426                                                 if (!box_items.Contains (item))
1427                                                         items.Add (item);
1428                                         foreach (ListViewItem item in box_items)
1429                                                 if (!prev_selection.Contains (item))
1430                                                         items.Add (item);
1431                                         break;
1432
1433                                 case BoxSelect.Shift:
1434                                         items = box_items;
1435                                         foreach (ListViewItem item in box_items)
1436                                                 prev_selection.Remove (item);
1437                                         foreach (ListViewItem item in prev_selection)
1438                                                 items.Add (item);
1439                                         break;
1440
1441                                 default:
1442                                         throw new Exception ("Unexpected Selection mode: " + box_select_mode);
1443                                 }
1444
1445                                 SuspendLayout ();
1446                                 owner.SelectItems (items);
1447                                 ResumeLayout ();
1448
1449                                 return true;
1450                         }
1451
1452                         private void ToggleCheckState (ListViewItem item)
1453                         {
1454                                 CheckState curr_state = item.Checked ?  CheckState.Checked : CheckState.Unchecked;
1455                                 item.Checked = !item.Checked;
1456                                 CheckState new_state = item.Checked ?  CheckState.Checked : CheckState.Unchecked;
1457
1458                                 ItemCheckEventArgs ice = new ItemCheckEventArgs (item.Index, curr_state, new_state);
1459                                 owner.OnItemCheck (ice);
1460                         }
1461
1462                         private void ItemsMouseDown (object sender, MouseEventArgs me)
1463                         {
1464                                 if (owner.items.Count == 0)
1465                                         return;
1466
1467                                 Point pt = new Point (me.X, me.Y);
1468                                 foreach (ListViewItem item in owner.items) {
1469                                         if (me.Clicks == 1 && item.CheckRectReal.Contains (pt)) {
1470                                                 checking = true;
1471                                                 if (me.Clicks > 1)
1472                                                         return;
1473                                                 ToggleCheckState (item);
1474                                                 return;
1475                                         }
1476
1477                                         if (owner.View == View.Details && !owner.FullRowSelect) {
1478                                                 if (item.GetBounds (ItemBoundsPortion.Label).Contains (pt)) {
1479                                                         clicked_item = item;
1480                                                         break;
1481                                                 }
1482                                         } else {
1483                                                 if (item.Bounds.Contains (pt)) {
1484                                                         clicked_item = item;
1485                                                         break;
1486                                                 }
1487                                         }
1488                                 }
1489
1490
1491                                 if (clicked_item != null) {
1492                                         owner.SetFocusedItem (clicked_item);
1493                                         bool changed = !clicked_item.Selected;
1494                                         if (owner.MultiSelect)
1495                                                 owner.UpdateMultiSelection (clicked_item.Index);
1496                                         else
1497                                                 clicked_item.Selected = true;
1498                                 
1499                                         if (changed)
1500                                                 owner.OnSelectedIndexChanged (EventArgs.Empty);
1501
1502                                         // Raise double click if the item was clicked. On MS the
1503                                         // double click is only raised if you double click an item
1504                                         if (me.Clicks > 1) {
1505                                                 owner.OnDoubleClick (EventArgs.Empty);
1506                                                 if (owner.CheckBoxes)
1507                                                         ToggleCheckState (clicked_item);
1508                                         } else if (me.Clicks == 1) {
1509                                                 owner.OnClick (EventArgs.Empty);
1510                                                 if (owner.LabelEdit && !changed)
1511                                                         BeginEdit (clicked_item); // this is probably not the correct place to execute BeginEdit
1512                                         }
1513                                 } else {
1514                                         if (owner.MultiSelect) {
1515                                                 Keys mods = XplatUI.State.ModifierKeys;
1516                                                 if ((mods & Keys.Shift) != 0)
1517                                                         box_select_mode = BoxSelect.Shift;
1518                                                 else if ((mods & Keys.Control) != 0)
1519                                                         box_select_mode = BoxSelect.Control;
1520                                                 else
1521                                                         box_select_mode = BoxSelect.Normal;
1522                                                 box_select_start = pt; 
1523                                                 prev_selection = owner.SelectedItems.List;
1524                                         } else if (owner.SelectedItems.Count > 0) {
1525                                                 owner.SelectedItems.Clear ();
1526                                                 owner.OnSelectedIndexChanged (EventArgs.Empty);
1527                                         }
1528                                 }
1529                         }
1530
1531                         private void ItemsMouseMove (object sender, MouseEventArgs me)
1532                         {
1533                                 if (PerformBoxSelection (new Point (me.X, me.Y)))
1534                                         return;
1535
1536                                 if (owner.HoverSelection && hover_processed) {
1537
1538                                         Point pt = PointToClient (Control.MousePosition);
1539                                         ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1540                                         if (item == null || item.Selected)
1541                                                 return;
1542
1543                                         hover_processed = false;
1544                                         XplatUI.ResetMouseHover (Handle);
1545                                 }
1546                         }
1547
1548
1549                         private void ItemsMouseHover (object sender, EventArgs e)
1550                         {
1551                                 if (Capture || !owner.HoverSelection)
1552                                         return;
1553
1554                                 hover_processed = true;
1555                                 Point pt = PointToClient (Control.MousePosition);
1556                                 ListViewItem item = owner.GetItemAt (pt.X, pt.Y);
1557
1558                                 if (item == null)
1559                                         return;
1560
1561                                 item.Selected = true;
1562                                 owner.OnSelectedIndexChanged (new EventArgs ());
1563                         }
1564
1565                         private void ItemsMouseUp (object sender, MouseEventArgs me)
1566                         {
1567                                 Capture = false;
1568                                 if (owner.Items.Count == 0)
1569                                         return;
1570
1571                                 Point pt = new Point (me.X, me.Y);
1572
1573                                 Rectangle rect = Rectangle.Empty;
1574                                 if (clicked_item != null) {
1575                                         if (owner.view == View.Details && !owner.full_row_select)
1576                                                 rect = clicked_item.GetBounds (ItemBoundsPortion.Label);
1577                                         else
1578                                                 rect = clicked_item.Bounds;
1579
1580                                         if (rect.Contains (pt)) {
1581                                                 switch (owner.activation) {
1582                                                 case ItemActivation.OneClick:
1583                                                         owner.OnItemActivate (EventArgs.Empty);
1584                                                         break;
1585
1586                                                 case ItemActivation.TwoClick:
1587                                                         if (last_clicked_item == clicked_item) {
1588                                                                 owner.OnItemActivate (EventArgs.Empty);
1589                                                                 last_clicked_item = null;
1590                                                         } else
1591                                                                 last_clicked_item = clicked_item;
1592                                                         break;
1593                                                 default:
1594                                                         // DoubleClick activation is handled in another handler
1595                                                         break;
1596                                                 }
1597                                         }
1598                                 } else if (!checking && owner.SelectedItems.Count > 0 && BoxSelectRectangle.Size.IsEmpty) {
1599                                         // Need this to clean up background clicks
1600                                         owner.SelectedItems.Clear ();
1601                                         owner.OnSelectedIndexChanged (EventArgs.Empty);
1602                                 }
1603
1604                                 clicked_item = null;
1605                                 box_select_start = Point.Empty;
1606                                 BoxSelectRectangle = Rectangle.Empty;
1607                                 prev_selection = null;
1608                                 box_select_mode = BoxSelect.None;
1609                                 checking = false;
1610                         }
1611                         
1612                         internal void LabelEditFinished (object sender, EventArgs e)
1613                         {
1614                                 EndEdit (edit_item);
1615                         }
1616                         
1617                         internal void BeginEdit (ListViewItem item)
1618                         {
1619                                 if (edit_item != null)
1620                                         EndEdit (edit_item);
1621                                 
1622                                 if (edit_text_box == null) {
1623                                         edit_text_box = new ListViewLabelEditTextBox ();
1624                                         edit_text_box.BorderStyle = BorderStyle.FixedSingle;
1625                                         edit_text_box.EditingFinished += new EventHandler (LabelEditFinished);
1626                                         edit_text_box.Visible = false;
1627                                         Controls.Add (edit_text_box);
1628                                 }
1629                                 
1630                                 item.EnsureVisible();
1631                                 
1632                                 edit_text_box.Reset ();
1633                                 
1634                                 switch (owner.view) {
1635                                         case View.List:
1636                                         case View.SmallIcon:
1637                                         case View.Details:
1638                                                 edit_text_box.TextAlign = HorizontalAlignment.Left;
1639                                                 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
1640                                                 SizeF sizef = DeviceContext.MeasureString (item.Text, item.Font);
1641                                                 edit_text_box.Width = (int)sizef.Width + 4;
1642                                                 edit_text_box.MaxWidth = owner.ClientRectangle.Width - edit_text_box.Bounds.X;
1643                                                 edit_text_box.WordWrap = false;
1644                                                 edit_text_box.Multiline = false;
1645                                                 break;
1646                                         case View.LargeIcon:
1647                                                 edit_text_box.TextAlign = HorizontalAlignment.Center;
1648                                                 edit_text_box.Bounds = item.GetBounds (ItemBoundsPortion.Label);
1649                                                 sizef = DeviceContext.MeasureString (item.Text, item.Font);
1650                                                 edit_text_box.Width = (int)sizef.Width + 4;
1651                                                 edit_text_box.MaxWidth = item.GetBounds(ItemBoundsPortion.Entire).Width;
1652                                                 edit_text_box.MaxHeight = owner.ClientRectangle.Height - edit_text_box.Bounds.Y;
1653                                                 edit_text_box.WordWrap = true;
1654                                                 edit_text_box.Multiline = true;
1655                                                 break;
1656                                 }
1657                                 
1658                                 edit_text_box.Text = item.Text;
1659                                 edit_text_box.Font = item.Font;
1660                                 edit_text_box.Visible = true;
1661                                 edit_text_box.Focus ();
1662                                 edit_text_box.SelectAll ();
1663                                 
1664                                 edit_args = new LabelEditEventArgs (owner.Items.IndexOf(edit_item));
1665                                 owner.OnBeforeLabelEdit (edit_args);
1666                                 
1667                                 if (edit_args.CancelEdit)
1668                                         EndEdit (item);
1669                                 
1670                                 edit_item = item;
1671                         }
1672                         
1673                         internal void EndEdit (ListViewItem item)
1674                         {
1675                                 if (edit_text_box != null && edit_text_box.Visible) {
1676                                         edit_text_box.Visible = false;
1677                                 }
1678                                 
1679                                 if (edit_item != null && edit_item == item) {
1680                                         owner.OnAfterLabelEdit (edit_args);
1681                                         
1682                                         if (!edit_args.CancelEdit) {
1683                                                 if (edit_args.Label != null)
1684                                                         edit_item.Text = edit_args.Label;
1685                                                 else
1686                                                         edit_item.Text = edit_text_box.Text;
1687                                         }
1688                                         
1689                                 }
1690                                 
1691                                 
1692                                 edit_item = null;
1693                         }
1694
1695                         internal override void OnPaintInternal (PaintEventArgs pe)
1696                         {
1697                                 ThemeEngine.Current.DrawListViewItems (pe.Graphics, pe.ClipRectangle, owner);
1698                         }
1699
1700                         internal override void OnGotFocusInternal (EventArgs e)
1701                         {
1702                                 owner.Focus ();
1703                         }
1704                 }
1705                 
1706                 internal class ListViewLabelEditTextBox : TextBox
1707                 {
1708                         int max_width = -1;
1709                         int min_width = -1;
1710                         
1711                         int max_height = -1;
1712                         int min_height = -1;
1713                         
1714                         int old_number_lines = 1;
1715                         
1716                         SizeF text_size_one_char;
1717                         
1718                         public ListViewLabelEditTextBox ()
1719                         {
1720                                 min_height = DefaultSize.Height;
1721                                 text_size_one_char = DeviceContext.MeasureString ("B", Font);
1722                         }
1723                         
1724                         public int MaxWidth {
1725                                 set {
1726                                         if (value < min_width)
1727                                                 max_width = min_width;
1728                                         else
1729                                                 max_width = value;
1730                                 }
1731                         }
1732                         
1733                         public int MaxHeight {
1734                                 set {
1735                                         if (value < min_height)
1736                                                 max_height = min_height;
1737                                         else
1738                                                 max_height = value;
1739                                 }
1740                         }
1741                         
1742                         public new int Width {
1743                                 get {
1744                                         return base.Width;
1745                                 }
1746                                 set {
1747                                         min_width = value;
1748                                         base.Width = value;
1749                                 }
1750                         }
1751                         
1752                         public override Font Font {
1753                                 get {
1754                                         return base.Font;
1755                                 }
1756                                 set {
1757                                         base.Font = value;
1758                                         text_size_one_char = DeviceContext.MeasureString ("B", Font);
1759                                 }
1760                         }
1761                         
1762                         protected override void OnTextChanged (EventArgs e)
1763                         {
1764                                 SizeF text_size = DeviceContext.MeasureString (Text, Font);
1765                                 
1766                                 int new_width = (int)text_size.Width + 8;
1767                                 
1768                                 if (!Multiline)
1769                                         ResizeTextBoxWidth (new_width);
1770                                 else {
1771                                         if (Width != max_width)
1772                                                 ResizeTextBoxWidth (new_width);
1773                                         
1774                                         int number_lines = Lines.Length;
1775                                         
1776                                         if (number_lines != old_number_lines) {
1777                                                 int new_height = number_lines * (int)text_size_one_char.Height + 4;
1778                                                 old_number_lines = number_lines;
1779                                                 
1780                                                 ResizeTextBoxHeight (new_height);
1781                                         }
1782                                 }
1783                                 
1784                                 base.OnTextChanged (e);
1785                         }
1786                         
1787                         protected override bool IsInputKey (Keys key_data)
1788                         {
1789                                 if ((key_data & Keys.Alt) == 0) {
1790                                         switch (key_data & Keys.KeyCode) {
1791                                                 case Keys.Enter:
1792                                                         return true;
1793                                         }
1794                                 }
1795                                 return base.IsInputKey (key_data);
1796                         }
1797                         
1798                         protected override void OnKeyDown (KeyEventArgs e)
1799                         {
1800                                 if (e.KeyCode == Keys.Return && Visible) {
1801                                         this.Visible = false;
1802                                         OnEditingFinished (e);
1803                                 }
1804                         }
1805                         
1806                         protected override void OnLostFocus (EventArgs e)
1807                         {
1808                                 if (Visible) {
1809                                         OnEditingFinished (e);
1810                                 }
1811                         }
1812                         
1813                         protected void OnEditingFinished (EventArgs e)
1814                         {
1815                                 if (EditingFinished != null)
1816                                         EditingFinished (this, EventArgs.Empty);
1817                         }
1818                         
1819                         private void ResizeTextBoxWidth (int new_width)
1820                         {
1821                                 if (new_width > max_width)
1822                                         base.Width = max_width;
1823                                 else 
1824                                 if (new_width >= min_width)
1825                                         base.Width = new_width;
1826                                 else
1827                                         base.Width = min_width;
1828                         }
1829                         
1830                         private void ResizeTextBoxHeight (int new_height)
1831                         {
1832                                 if (new_height > max_height)
1833                                         base.Height = max_height;
1834                                 else 
1835                                 if (new_height >= min_height)
1836                                         base.Height = new_height;
1837                                 else
1838                                         base.Height = min_height;
1839                         }
1840                         
1841                         public void Reset ()
1842                         {
1843                                 max_width = -1;
1844                                 min_width = -1;
1845                                 
1846                                 max_height = -1;
1847                                 
1848                                 old_number_lines = 1;
1849                                 
1850                                 Text = String.Empty;
1851                                 
1852                                 Size = DefaultSize;
1853                         }
1854                         
1855                         public event EventHandler EditingFinished;
1856                 }
1857
1858                 internal override void OnPaintInternal (PaintEventArgs pe)
1859                 {
1860                         if (updating)
1861                                 return; 
1862                                 
1863                         CalculateScrollBars ();
1864                 }
1865
1866                 void FocusChanged (object o, EventArgs args)
1867                 {
1868                         if (Items.Count == 0)
1869                                 return;
1870
1871                         if (FocusedItem == null)
1872                                 SetFocusedItem (Items [0]);
1873
1874                         item_control.Invalidate (FocusedItem.Bounds);
1875                 }
1876
1877                 private void ListView_MouseWheel (object sender, MouseEventArgs me)
1878                 {
1879                         if (Items.Count == 0)
1880                                 return;
1881
1882                         int lines = me.Delta / 120;
1883
1884                         if (lines == 0)
1885                                 return;
1886
1887                         switch (View) {
1888                         case View.Details:
1889                         case View.SmallIcon:
1890                                 Scroll (v_scroll, -Items [0].Bounds.Height * SystemInformation.MouseWheelScrollLines * lines);
1891                                 break;
1892                         case View.LargeIcon:
1893                                 Scroll (v_scroll, -(Items [0].Bounds.Height + ThemeEngine.Current.ListViewVerticalSpacing)  * lines);
1894                                 break;
1895                         case View.List:
1896                                 Scroll (h_scroll, -Items [0].Bounds.Width * lines);
1897                                 break;
1898                         }
1899                 }
1900
1901                 private void ListView_SizeChanged (object sender, EventArgs e)
1902                 {
1903                         CalculateListView (alignment);
1904                 }
1905                 
1906                 private void SetFocusedItem (ListViewItem item)
1907                 {
1908                         if (focused_item != null)
1909                                 focused_item.Focused = false;
1910                         
1911                         if (item != null)
1912                                 item.Focused = true;
1913                                 
1914                         focused_item = item;
1915                 }
1916
1917                 private void HorizontalScroller (object sender, EventArgs e)
1918                 {
1919                         item_control.EndEdit (item_control.edit_item);
1920                         
1921                         // Avoid unnecessary flickering, when button is
1922                         // kept pressed at the end
1923                         if (h_marker != h_scroll.Value) {
1924                                 
1925                                 int pixels =  h_marker - h_scroll.Value;
1926                                 
1927                                 h_marker = h_scroll.Value;
1928                                 if (header_control.Visible)
1929                                         XplatUI.ScrollWindow (header_control.Handle, pixels, 0, false);
1930
1931                                 XplatUI.ScrollWindow (item_control.Handle, pixels, 0, false);
1932                         }
1933                 }
1934
1935                 private void VerticalScroller (object sender, EventArgs e)
1936                 {
1937                         item_control.EndEdit (item_control.edit_item);
1938                         
1939                         // Avoid unnecessary flickering, when button is
1940                         // kept pressed at the end
1941                         if (v_marker != v_scroll.Value) {
1942                                 int pixels =  v_marker - v_scroll.Value;
1943                                 Rectangle area = item_control.ClientRectangle;
1944                                 v_marker = v_scroll.Value;
1945                                 XplatUI.ScrollWindow (item_control.Handle, area, 0, pixels, false);
1946                         }
1947                 }
1948                 #endregion      // Internal Methods Properties
1949
1950                 #region Protected Methods
1951                 protected override void CreateHandle ()
1952                 {
1953                         base.CreateHandle ();
1954                         for (int i = 0; i < SelectedItems.Count; i++)
1955                                 OnSelectedIndexChanged (EventArgs.Empty);
1956                 }
1957
1958                 protected override void Dispose (bool disposing)
1959                 {                       
1960                         if (disposing) {                        
1961                                 h_scroll.Dispose ();
1962                                 v_scroll.Dispose ();
1963                                 
1964                                 large_image_list = null;
1965                                 small_image_list = null;
1966                                 state_image_list = null;
1967                         }
1968                         
1969                         base.Dispose (disposing);
1970                 }
1971
1972                 protected override bool IsInputKey (Keys keyData)
1973                 {
1974                         switch (keyData) {
1975                         case Keys.Up:
1976                         case Keys.Down:
1977                         case Keys.PageUp:
1978                         case Keys.PageDown:
1979                         case Keys.Right:
1980                         case Keys.Left:
1981                         case Keys.End:
1982                         case Keys.Home:                         
1983                                 return true;
1984
1985                         default:
1986                                 break;
1987                         }
1988                         
1989                         return base.IsInputKey (keyData);
1990                 }
1991
1992                 protected virtual void OnAfterLabelEdit (LabelEditEventArgs e)
1993                 {
1994                         if (AfterLabelEdit != null)
1995                                 AfterLabelEdit (this, e);
1996                 }
1997
1998                 protected virtual void OnBeforeLabelEdit (LabelEditEventArgs e)
1999                 {
2000                         if (BeforeLabelEdit != null)
2001                                 BeforeLabelEdit (this, e);
2002                 }
2003
2004                 protected virtual void OnColumnClick (ColumnClickEventArgs e)
2005                 {
2006                         if (ColumnClick != null)
2007                                 ColumnClick (this, e);
2008                 }
2009
2010                 protected override void OnEnabledChanged (EventArgs e)
2011                 {
2012                         base.OnEnabledChanged (e);
2013                 }
2014
2015                 protected override void OnFontChanged (EventArgs e)
2016                 {
2017                         base.OnFontChanged (e);
2018                         Redraw (true);
2019                 }
2020
2021                 protected override void OnHandleCreated (EventArgs e)
2022                 {
2023                         base.OnHandleCreated (e);
2024                         Sort ();
2025                 }
2026
2027                 protected override void OnHandleDestroyed (EventArgs e)
2028                 {
2029                         base.OnHandleDestroyed (e);
2030                 }
2031
2032                 protected virtual void OnItemActivate (EventArgs e)
2033                 {
2034                         if (ItemActivate != null)
2035                                 ItemActivate (this, e);
2036                 }
2037
2038                 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
2039                 {
2040                         if (ItemCheck != null)
2041                                 ItemCheck (this, ice);
2042                 }
2043
2044                 protected virtual void OnItemDrag (ItemDragEventArgs e)
2045                 {
2046                         if (ItemDrag != null)
2047                                 ItemDrag (this, e);
2048                 }
2049
2050                 protected virtual void OnSelectedIndexChanged (EventArgs e)
2051                 {
2052                         if (SelectedIndexChanged != null)
2053                                 SelectedIndexChanged (this, e);
2054                 }
2055
2056                 protected override void OnSystemColorsChanged (EventArgs e)
2057                 {
2058                         base.OnSystemColorsChanged (e);
2059                 }
2060
2061                 protected void RealizeProperties ()
2062                 {
2063                         // FIXME: TODO
2064                 }
2065
2066                 protected void UpdateExtendedStyles ()
2067                 {
2068                         // FIXME: TODO
2069                 }
2070
2071                 protected override void WndProc (ref Message m)
2072                 {
2073                         base.WndProc (ref m);
2074                 }
2075                 #endregion // Protected Methods
2076
2077                 #region Public Instance Methods
2078                 public void ArrangeIcons ()
2079                 {
2080                         ArrangeIcons (this.alignment);
2081                 }
2082
2083                 public void ArrangeIcons (ListViewAlignment alignment)
2084                 {
2085                         // Icons are arranged only if view is set to LargeIcon or SmallIcon
2086                         if (view == View.LargeIcon || view == View.SmallIcon) {
2087                                 this.CalculateListView (alignment);
2088                                 // we have done the calculations already
2089                                 this.Redraw (false);
2090                         }
2091                 }
2092
2093                 public void BeginUpdate ()
2094                 {
2095                         // flag to avoid painting
2096                         updating = true;
2097                 }
2098
2099                 public void Clear ()
2100                 {
2101                         columns.Clear ();
2102                         items.Clear (); // Redraw (true) called here                    
2103                 }
2104
2105                 public void EndUpdate ()
2106                 {
2107                         // flag to avoid painting
2108                         updating = false;
2109
2110                         // probably, now we need a redraw with recalculations
2111                         this.Redraw (true);
2112                 }
2113
2114                 public void EnsureVisible (int index)
2115                 {
2116                         if (index < 0 || index >= items.Count || scrollable == false)
2117                                 return;
2118
2119                         Rectangle view_rect = item_control.ClientRectangle;
2120                         Rectangle bounds = items [index].Bounds;
2121
2122                         if (view_rect.Contains (bounds))
2123                                 return;
2124
2125                         if (bounds.Left < 0)
2126                                 h_scroll.Value += bounds.Left;
2127                         else if (bounds.Right > view_rect.Right)
2128                                 h_scroll.Value += (bounds.Right - view_rect.Right);
2129
2130                         if (bounds.Top < 0)
2131                                 v_scroll.Value += bounds.Top;
2132                         else if (bounds.Bottom > view_rect.Bottom)
2133                                 v_scroll.Value += (bounds.Bottom - view_rect.Bottom);
2134                 }
2135                 
2136                 public ListViewItem GetItemAt (int x, int y)
2137                 {
2138                         foreach (ListViewItem item in items) {
2139                                 if (item.Bounds.Contains (x, y))
2140                                         return item;
2141                         }
2142                         return null;
2143                 }
2144
2145                 public Rectangle GetItemRect (int index)
2146                 {
2147                         return GetItemRect (index, ItemBoundsPortion.Entire);
2148                 }
2149
2150                 public Rectangle GetItemRect (int index, ItemBoundsPortion portion)
2151                 {
2152                         if (index < 0 || index >= items.Count)
2153                                 throw new IndexOutOfRangeException ("index");
2154
2155                         return items [index].GetBounds (portion);
2156                 }
2157
2158                 public void Sort ()
2159                 {
2160                         Sort (true);
2161                 }
2162
2163                 // we need this overload to reuse the logic for sorting, while allowing
2164                 // redrawing to be done by caller or have it done by this method when
2165                 // sorting is really performed
2166                 //
2167                 // ListViewItemCollection's Add and AddRange methods call this overload
2168                 // with redraw set to false, as they take care of redrawing themselves
2169                 // (they even want to redraw the listview if no sort is performed, as 
2170                 // an item was added), while ListView.Sort () only wants to redraw if 
2171                 // sorting was actually performed
2172                 private void Sort (bool redraw)
2173                 {
2174                         if (!IsHandleCreated || item_sorter == null) {
2175                                 return;
2176                         }
2177                         
2178                         items.Sort (item_sorter);
2179                         if (redraw)
2180                                 this.Redraw (true);
2181                 }
2182
2183                 public override string ToString ()
2184                 {
2185                         int count = this.Items.Count;
2186
2187                         if (count == 0)
2188                                 return string.Format ("System.Windows.Forms.ListView, Items.Count: 0");
2189                         else
2190                                 return string.Format ("System.Windows.Forms.ListView, Items.Count: {0}, Items[0]: {1}", count, this.Items [0].ToString ());
2191                 }
2192                 #endregion      // Public Instance Methods
2193
2194
2195                 #region Subclasses
2196
2197                 class HeaderControl : Control {
2198
2199                         ListView owner;
2200                         bool column_resize_active = false;
2201                         ColumnHeader resize_column;
2202                         ColumnHeader clicked_column;
2203                         ColumnHeader drag_column;
2204                         int drag_x;
2205                         int drag_to_index = -1;
2206
2207                         public HeaderControl (ListView owner)
2208                         {
2209                                 this.owner = owner;
2210                                 MouseDown += new MouseEventHandler (HeaderMouseDown);
2211                                 MouseMove += new MouseEventHandler (HeaderMouseMove);
2212                                 MouseUp += new MouseEventHandler (HeaderMouseUp);
2213                         }
2214
2215                         private ColumnHeader ColumnAtX (int x)
2216                         {
2217                                 Point pt = new Point (x, 0);
2218                                 ColumnHeader result = null;
2219                                 foreach (ColumnHeader col in owner.Columns) {
2220                                         if (col.Rect.Contains (pt)) {
2221                                                 result = col;
2222                                                 break;
2223                                         }
2224                                 }
2225                                 return result;
2226                         }
2227
2228                         private int GetReorderedIndex (ColumnHeader col)
2229                         {
2230                                 if (owner.reordered_column_indices == null)
2231                                         return col.Index;
2232                                 else
2233                                         for (int i = 0; i < owner.Columns.Count; i++)
2234                                                 if (owner.reordered_column_indices [i] == col.Index)
2235                                                         return i;
2236                                 throw new Exception ("Column index missing from reordered array");
2237                         }
2238
2239                         private void HeaderMouseDown (object sender, MouseEventArgs me)
2240                         {
2241                                 if (resize_column != null) {
2242                                         column_resize_active = true;
2243                                         Capture = true;
2244                                         return;
2245                                 }
2246
2247                                 clicked_column = ColumnAtX (me.X + owner.h_marker);
2248
2249                                 if (clicked_column != null) {
2250                                         Capture = true;
2251                                         if (owner.AllowColumnReorder) {
2252                                                 drag_x = me.X;
2253                                                 drag_column = (ColumnHeader) (clicked_column as ICloneable).Clone ();
2254                                                 drag_column.column_rect = clicked_column.Rect;
2255                                                 drag_to_index = GetReorderedIndex (clicked_column);
2256                                         }
2257                                         clicked_column.pressed = true;
2258                                         Rectangle bounds = clicked_column.Rect;
2259                                         bounds.X -= owner.h_marker;
2260                                         Invalidate (bounds);
2261                                         return;
2262                                 }
2263                         }
2264
2265                         private void HeaderMouseMove (object sender, MouseEventArgs me)
2266                         {
2267                                 Point pt = new Point (me.X + owner.h_marker, me.Y);
2268
2269                                 if (column_resize_active)  {
2270                                         resize_column.Width = pt.X - resize_column.X;
2271                                         if (resize_column.Width < 0)
2272                                                 resize_column.Width = 0;
2273                                         return;
2274                                 }
2275
2276                                 resize_column = null;
2277
2278                                 if (clicked_column != null) {
2279                                         if (owner.AllowColumnReorder) {
2280                                                 Rectangle r;
2281
2282                                                 r = drag_column.column_rect;
2283                                                 r.X = clicked_column.Rect.X + me.X - drag_x;
2284                                                 drag_column.column_rect = r;
2285
2286                                                 int x = me.X + owner.h_marker;
2287                                                 ColumnHeader over = ColumnAtX (x);
2288                                                 if (over == null)
2289                                                         drag_to_index = owner.Columns.Count;
2290                                                 else if (x < over.X + over.Width / 2)
2291                                                         drag_to_index = GetReorderedIndex (over);
2292                                                 else
2293                                                         drag_to_index = GetReorderedIndex (over) + 1;
2294                                                 Invalidate ();
2295                                         } else {
2296                                                 ColumnHeader over = ColumnAtX (me.X + owner.h_marker);
2297                                                 bool pressed = clicked_column.pressed;
2298                                                 clicked_column.pressed = over == clicked_column;
2299                                                 if (clicked_column.pressed ^ pressed) {
2300                                                         Rectangle bounds = clicked_column.Rect;
2301                                                         bounds.X -= owner.h_marker;
2302                                                         Invalidate (bounds);
2303                                                 }
2304                                         }
2305                                         return;
2306                                 }
2307
2308                                 for (int i = 0; i < owner.Columns.Count; i++) {
2309                                         Rectangle zone = owner.Columns [i].Rect;
2310                                         zone.X = zone.Right - 5;
2311                                         zone.Width = 10;
2312                                         if (zone.Contains (pt)) {
2313                                                 if (i < owner.Columns.Count - 1 && owner.Columns [i + 1].Width == 0)
2314                                                         i++;
2315                                                 resize_column = owner.Columns [i];
2316                                                 break;
2317                                         }
2318                                 }
2319
2320                                 if (resize_column == null)
2321                                         Cursor = Cursors.Default;
2322                                 else
2323                                         Cursor = Cursors.VSplit;
2324                         }
2325
2326                         void HeaderMouseUp (object sender, MouseEventArgs me)
2327                         {
2328                                 Capture = false;
2329
2330                                 if (column_resize_active) {
2331                                         column_resize_active = false;
2332                                         resize_column = null;
2333                                         Cursor = Cursors.Default;
2334                                         return;
2335                                 }
2336
2337                                 if (clicked_column != null && clicked_column.pressed) {
2338                                         clicked_column.pressed = false;
2339                                         Rectangle bounds = clicked_column.Rect;
2340                                         bounds.X -= owner.h_marker;
2341                                         Invalidate (bounds);
2342                                         owner.OnColumnClick (new ColumnClickEventArgs (clicked_column.Index));
2343                                 }
2344
2345                                 if (drag_column != null && owner.AllowColumnReorder) {
2346                                         drag_column = null;
2347                                         if (drag_to_index > GetReorderedIndex (clicked_column))
2348                                                 drag_to_index--;
2349                                         if (owner.GetReorderedColumn (drag_to_index) != clicked_column)
2350                                                 owner.ReorderColumn (clicked_column, drag_to_index);
2351                                         drag_to_index = -1;
2352                                         Invalidate ();
2353                                 }
2354
2355                                 clicked_column = null;
2356                         }
2357
2358                         internal override void OnPaintInternal (PaintEventArgs pe)
2359                         {
2360                                 if (owner.updating)
2361                                         return; 
2362                                 
2363                                 Theme theme = ThemeEngine.Current;
2364                                 theme.DrawListViewHeader (pe.Graphics, pe.ClipRectangle, this.owner);
2365
2366                                 if (drag_column == null)
2367                                         return;
2368
2369                                 int target_x;
2370                                 if (drag_to_index == owner.Columns.Count)
2371                                         target_x = owner.GetReorderedColumn (drag_to_index - 1).Rect.Right - owner.h_marker;
2372                                 else
2373                                         target_x = owner.GetReorderedColumn (drag_to_index).Rect.X - owner.h_marker;
2374                                 theme.DrawListViewHeaderDragDetails (pe.Graphics, owner, drag_column, target_x);
2375                         }
2376
2377                         protected override void WndProc (ref Message m)
2378                         {
2379                                 switch ((Msg)m.Msg) {
2380                                 case Msg.WM_SETFOCUS:
2381                                         owner.Focus ();
2382                                         break;
2383                                 default:
2384                                         base.WndProc (ref m);
2385                                         break;
2386                                 }
2387                         }
2388                 }
2389
2390                 private class ItemComparer : IComparer {
2391                         readonly SortOrder sort_order;
2392
2393                         public ItemComparer (SortOrder sortOrder)
2394                         {
2395                                 sort_order = sortOrder;
2396                         }
2397
2398                         public int Compare (object x, object y)
2399                         {
2400                                 ListViewItem item_x = x as ListViewItem;
2401                                 ListViewItem item_y = y as ListViewItem;
2402                                 if (sort_order == SortOrder.Ascending)
2403                                         return String.Compare (item_x.Text, item_y.Text);
2404                                 else
2405                                         return String.Compare (item_y.Text, item_x.Text);
2406                         }
2407                 }
2408
2409                 public class CheckedIndexCollection : IList, ICollection, IEnumerable
2410                 {
2411                         private readonly ListView owner;
2412
2413                         #region Public Constructor
2414                         public CheckedIndexCollection (ListView owner)
2415                         {
2416                                 this.owner = owner;
2417                         }
2418                         #endregion      // Public Constructor
2419
2420                         #region Public Properties
2421                         [Browsable (false)]
2422                         public int Count {
2423                                 get { return owner.CheckedItems.Count; }
2424                         }
2425
2426                         public bool IsReadOnly {
2427                                 get { return true; }
2428                         }
2429
2430                         public int this [int index] {
2431                                 get {
2432                                         int [] indices = GetIndices ();
2433                                         if (index < 0 || index >= indices.Length)
2434                                                 throw new ArgumentOutOfRangeException ("index");
2435                                         return indices [index];
2436                                 }
2437                         }
2438
2439                         bool ICollection.IsSynchronized {
2440                                 get { return false; }
2441                         }
2442
2443                         object ICollection.SyncRoot {
2444                                 get { return this; }
2445                         }
2446
2447                         bool IList.IsFixedSize {
2448                                 get { return true; }
2449                         }
2450
2451                         object IList.this [int index] {
2452                                 get { return this [index]; }
2453                                 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2454                         }
2455                         #endregion      // Public Properties
2456
2457                         #region Public Methods
2458                         public bool Contains (int checkedIndex)
2459                         {
2460                                 int [] indices = GetIndices ();
2461                                 for (int i = 0; i < indices.Length; i++) {
2462                                         if (indices [i] == checkedIndex)
2463                                                 return true;
2464                                 }
2465                                 return false;
2466                         }
2467
2468                         public IEnumerator GetEnumerator ()
2469                         {
2470                                 int [] indices = GetIndices ();
2471                                 return indices.GetEnumerator ();
2472                         }
2473
2474                         void ICollection.CopyTo (Array dest, int index)
2475                         {
2476                                 int [] indices = GetIndices ();
2477                                 Array.Copy (indices, 0, dest, index, indices.Length);
2478                         }
2479
2480                         int IList.Add (object value)
2481                         {
2482                                 throw new NotSupportedException ("Add operation is not supported.");
2483                         }
2484
2485                         void IList.Clear ()
2486                         {
2487                                 throw new NotSupportedException ("Clear operation is not supported.");
2488                         }
2489
2490                         bool IList.Contains (object checkedIndex)
2491                         {
2492                                 if (!(checkedIndex is int))
2493                                         return false;
2494                                 return Contains ((int) checkedIndex);
2495                         }
2496
2497                         int IList.IndexOf (object checkedIndex)
2498                         {
2499                                 if (!(checkedIndex is int))
2500                                         return -1;
2501                                 return IndexOf ((int) checkedIndex);
2502                         }
2503
2504                         void IList.Insert (int index, object value)
2505                         {
2506                                 throw new NotSupportedException ("Insert operation is not supported.");
2507                         }
2508
2509                         void IList.Remove (object value)
2510                         {
2511                                 throw new NotSupportedException ("Remove operation is not supported.");
2512                         }
2513
2514                         void IList.RemoveAt (int index)
2515                         {
2516                                 throw new NotSupportedException ("RemoveAt operation is not supported.");
2517                         }
2518
2519                         public int IndexOf (int checkedIndex)
2520                         {
2521                                 int [] indices = GetIndices ();
2522                                 for (int i = 0; i < indices.Length; i++) {
2523                                         if (indices [i] == checkedIndex)
2524                                                 return i;
2525                                 }
2526                                 return -1;
2527                         }
2528                         #endregion      // Public Methods
2529
2530                         private int [] GetIndices ()
2531                         {
2532                                 ArrayList checked_items = owner.CheckedItems.List;
2533                                 int [] indices = new int [checked_items.Count];
2534                                 for (int i = 0; i < checked_items.Count; i++) {
2535                                         ListViewItem item = (ListViewItem) checked_items [i];
2536                                         indices [i] = item.Index;
2537                                 }
2538                                 return indices;
2539                         }
2540                 }       // CheckedIndexCollection
2541
2542                 public class CheckedListViewItemCollection : IList, ICollection, IEnumerable
2543                 {
2544                         private readonly ListView owner;
2545                         private ArrayList list;
2546
2547                         #region Public Constructor
2548                         public CheckedListViewItemCollection (ListView owner)
2549                         {
2550                                 this.owner = owner;
2551                                 this.owner.Items.Changed += new CollectionChangedHandler (
2552                                         ItemsCollection_Changed);
2553                         }
2554                         #endregion      // Public Constructor
2555
2556                         #region Public Properties
2557                         [Browsable (false)]
2558                         public int Count {
2559                                 get {
2560                                         if (!owner.CheckBoxes)
2561                                                 return 0;
2562                                         return List.Count;
2563                                 }
2564                         }
2565
2566                         public bool IsReadOnly {
2567                                 get { return true; }
2568                         }
2569
2570                         public ListViewItem this [int index] {
2571                                 get {
2572                                         ArrayList checked_items = List;
2573                                         if (index < 0 || index >= checked_items.Count)
2574                                                 throw new ArgumentOutOfRangeException ("index");
2575                                         return (ListViewItem) checked_items [index];
2576                                 }
2577                         }
2578
2579                         bool ICollection.IsSynchronized {
2580                                 get { return false; }
2581                         }
2582
2583                         object ICollection.SyncRoot {
2584                                 get { return this; }
2585                         }
2586
2587                         bool IList.IsFixedSize {
2588                                 get { return true; }
2589                         }
2590
2591                         object IList.this [int index] {
2592                                 get { return this [index]; }
2593                                 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2594                         }
2595                         #endregion      // Public Properties
2596
2597                         #region Public Methods
2598                         public bool Contains (ListViewItem item)
2599                         {
2600                                 if (!owner.CheckBoxes)
2601                                         return false;
2602                                 return List.Contains (item);
2603                         }
2604
2605                         public void CopyTo (Array dest, int index)
2606                         {
2607                                 if (!owner.CheckBoxes)
2608                                         return;
2609                                 List.CopyTo (dest, index);
2610                         }
2611
2612                         public IEnumerator GetEnumerator ()
2613                         {
2614                                 if (!owner.CheckBoxes)
2615                                         return (new ListViewItem [0]).GetEnumerator ();
2616                                 return List.GetEnumerator ();
2617                         }
2618
2619                         int IList.Add (object value)
2620                         {
2621                                 throw new NotSupportedException ("Add operation is not supported.");
2622                         }
2623
2624                         void IList.Clear ()
2625                         {
2626                                 throw new NotSupportedException ("Clear operation is not supported.");
2627                         }
2628
2629                         bool IList.Contains (object item)
2630                         {
2631                                 if (!(item is ListViewItem))
2632                                         return false;
2633                                 return Contains ((ListViewItem) item);
2634                         }
2635
2636                         int IList.IndexOf (object item)
2637                         {
2638                                 if (!(item is ListViewItem))
2639                                         return -1;
2640                                 return IndexOf ((ListViewItem) item);
2641                         }
2642
2643                         void IList.Insert (int index, object value)
2644                         {
2645                                 throw new NotSupportedException ("Insert operation is not supported.");
2646                         }
2647
2648                         void IList.Remove (object value)
2649                         {
2650                                 throw new NotSupportedException ("Remove operation is not supported.");
2651                         }
2652
2653                         void IList.RemoveAt (int index)
2654                         {
2655                                 throw new NotSupportedException ("RemoveAt operation is not supported.");
2656                         }
2657
2658                         public int IndexOf (ListViewItem item)
2659                         {
2660                                 if (!owner.CheckBoxes)
2661                                         return -1;
2662                                 return List.IndexOf (item);
2663                         }
2664                         #endregion      // Public Methods
2665
2666                         internal ArrayList List {
2667                                 get {
2668                                         if (list == null) {
2669                                                 list = new ArrayList ();
2670                                                 foreach (ListViewItem item in owner.Items) {
2671                                                         if (item.Checked)
2672                                                                 list.Add (item);
2673                                                 }
2674                                         }
2675                                         return list;
2676                                 }
2677                         }
2678
2679                         internal void Reset ()
2680                         {
2681                                 // force re-population of list
2682                                 list = null;
2683                         }
2684
2685                         private void ItemsCollection_Changed ()
2686                         {
2687                                 Reset ();
2688                         }
2689                 }       // CheckedListViewItemCollection
2690
2691                 public class ColumnHeaderCollection : IList, ICollection, IEnumerable
2692                 {
2693                         internal ArrayList list;
2694                         private ListView owner;
2695
2696                         #region Public Constructor
2697                         public ColumnHeaderCollection (ListView owner)
2698                         {
2699                                 list = new ArrayList ();
2700                                 this.owner = owner;
2701                         }
2702                         #endregion      // Public Constructor
2703
2704                         #region Public Properties
2705                         [Browsable (false)]
2706                         public int Count {
2707                                 get { return list.Count; }
2708                         }
2709
2710                         public bool IsReadOnly {
2711                                 get { return false; }
2712                         }
2713
2714                         public virtual ColumnHeader this [int index] {
2715                                 get {
2716                                         if (index < 0 || index >= list.Count)
2717                                                 throw new ArgumentOutOfRangeException ("index");
2718                                         return (ColumnHeader) list [index];
2719                                 }
2720                         }
2721
2722                         bool ICollection.IsSynchronized {
2723                                 get { return true; }
2724                         }
2725
2726                         object ICollection.SyncRoot {
2727                                 get { return this; }
2728                         }
2729
2730                         bool IList.IsFixedSize {
2731                                 get { return list.IsFixedSize; }
2732                         }
2733
2734                         object IList.this [int index] {
2735                                 get { return this [index]; }
2736                                 set { throw new NotSupportedException ("SetItem operation is not supported."); }
2737                         }
2738                         #endregion      // Public Properties
2739
2740                         #region Public Methods
2741                         public virtual int Add (ColumnHeader value)
2742                         {
2743                                 int idx;
2744                                 value.owner = this.owner;
2745                                 idx = list.Add (value);
2746                                 if (owner.IsHandleCreated) {
2747                                         owner.Redraw (true); 
2748                                 }
2749                                 return idx;
2750                         }
2751
2752                         public virtual ColumnHeader Add (string str, int width, HorizontalAlignment textAlign)
2753                         {
2754                                 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2755                                 this.Add (colHeader);                                                                   
2756                                 return colHeader;
2757                         }
2758
2759                         public virtual void AddRange (ColumnHeader [] values)
2760                         {
2761                                 foreach (ColumnHeader colHeader in values) {
2762                                         colHeader.owner = this.owner;
2763                                         Add (colHeader);
2764                                 }
2765                                 
2766                                 owner.Redraw (true); 
2767                         }
2768
2769                         public virtual void Clear ()
2770                         {
2771                                 list.Clear ();
2772                                 owner.Redraw (true);
2773                         }
2774
2775                         public bool Contains (ColumnHeader value)
2776                         {
2777                                 return list.Contains (value);
2778                         }
2779
2780                         public IEnumerator GetEnumerator ()
2781                         {
2782                                 return list.GetEnumerator ();
2783                         }
2784
2785                         void ICollection.CopyTo (Array dest, int index)
2786                         {
2787                                 list.CopyTo (dest, index);
2788                         }
2789
2790                         int IList.Add (object value)
2791                         {
2792                                 if (! (value is ColumnHeader)) {
2793                                         throw new ArgumentException ("Not of type ColumnHeader", "value");
2794                                 }
2795
2796                                 return this.Add ((ColumnHeader) value);
2797                         }
2798
2799                         bool IList.Contains (object value)
2800                         {
2801                                 if (! (value is ColumnHeader)) {
2802                                         throw new ArgumentException ("Not of type ColumnHeader", "value");
2803                                 }
2804
2805                                 return this.Contains ((ColumnHeader) value);
2806                         }
2807
2808                         int IList.IndexOf (object value)
2809                         {
2810                                 if (! (value is ColumnHeader)) {
2811                                         throw new ArgumentException ("Not of type ColumnHeader", "value");
2812                                 }
2813
2814                                 return this.IndexOf ((ColumnHeader) value);
2815                         }
2816
2817                         void IList.Insert (int index, object value)
2818                         {
2819                                 if (! (value is ColumnHeader)) {
2820                                         throw new ArgumentException ("Not of type ColumnHeader", "value");
2821                                 }
2822
2823                                 this.Insert (index, (ColumnHeader) value);
2824                         }
2825
2826                         void IList.Remove (object value)
2827                         {
2828                                 if (! (value is ColumnHeader)) {
2829                                         throw new ArgumentException ("Not of type ColumnHeader", "value");
2830                                 }
2831
2832                                 this.Remove ((ColumnHeader) value);
2833                         }
2834
2835                         public int IndexOf (ColumnHeader value)
2836                         {
2837                                 return list.IndexOf (value);
2838                         }
2839
2840                         public void Insert (int index, ColumnHeader value)
2841                         {
2842                                 // LAMESPEC: MSDOCS say greater than or equal to the value of the Count property
2843                                 // but it's really only greater.
2844                                 if (index < 0 || index > list.Count)
2845                                         throw new ArgumentOutOfRangeException ("index");
2846
2847                                 value.owner = this.owner;
2848                                 list.Insert (index, value);
2849                                 owner.Redraw (true);
2850                         }
2851
2852                         public void Insert (int index, string str, int width, HorizontalAlignment textAlign)
2853                         {
2854                                 ColumnHeader colHeader = new ColumnHeader (this.owner, str, textAlign, width);
2855                                 this.Insert (index, colHeader);
2856                         }
2857
2858                         public virtual void Remove (ColumnHeader column)
2859                         {
2860                                 // TODO: Update Column internal index ?
2861                                 list.Remove (column);
2862                                 owner.Redraw (true);
2863                         }
2864
2865                         public virtual void RemoveAt (int index)
2866                         {
2867                                 if (index < 0 || index >= list.Count)
2868                                         throw new ArgumentOutOfRangeException ("index");
2869
2870                                 // TODO: Update Column internal index ?
2871                                 list.RemoveAt (index);
2872                                 owner.Redraw (true);
2873                         }
2874                         #endregion      // Public Methods
2875                         
2876
2877                 }       // ColumnHeaderCollection
2878
2879                 public class ListViewItemCollection : IList, ICollection, IEnumerable
2880                 {
2881                         private readonly ArrayList list;
2882                         private readonly ListView owner;
2883
2884                         #region Public Constructor
2885                         public ListViewItemCollection (ListView owner)
2886                         {
2887                                 list = new ArrayList ();
2888                                 this.owner = owner;
2889                         }
2890                         #endregion      // Public Constructor
2891
2892                         #region Public Properties
2893                         [Browsable (false)]
2894                         public int Count {
2895                                 get { return list.Count; }
2896                         }
2897
2898                         public bool IsReadOnly {
2899                                 get { return false; }
2900                         }
2901
2902                         public virtual ListViewItem this [int displayIndex] {
2903                                 get {
2904                                         if (displayIndex < 0 || displayIndex >= list.Count)
2905                                                 throw new ArgumentOutOfRangeException ("displayIndex");
2906                                         return (ListViewItem) list [displayIndex];
2907                                 }
2908
2909                                 set {
2910                                         if (displayIndex < 0 || displayIndex >= list.Count)
2911                                                 throw new ArgumentOutOfRangeException ("displayIndex");
2912
2913                                         if (list.Contains (value))
2914                                                 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2915
2916                                         value.Owner = owner;
2917                                         list [displayIndex] = value;
2918                                         OnChange ();
2919
2920                                         owner.Redraw (true);
2921                                 }
2922                         }
2923
2924                         bool ICollection.IsSynchronized {
2925                                 get { return true; }
2926                         }
2927
2928                         object ICollection.SyncRoot {
2929                                 get { return this; }
2930                         }
2931
2932                         bool IList.IsFixedSize {
2933                                 get { return list.IsFixedSize; }
2934                         }
2935
2936                         object IList.this [int index] {
2937                                 get { return this [index]; }
2938                                 set {
2939                                         if (value is ListViewItem)
2940                                                 this [index] = (ListViewItem) value;
2941                                         else
2942                                                 this [index] = new ListViewItem (value.ToString ());
2943                                         OnChange ();
2944                                 }
2945                         }
2946                         #endregion      // Public Properties
2947
2948                         #region Public Methods
2949                         public virtual ListViewItem Add (ListViewItem value)
2950                         {
2951                                 if (list.Contains (value))
2952                                         throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "value");
2953
2954                                 value.Owner = owner;
2955                                 list.Add (value);
2956
2957                                 owner.Sort (false);
2958                                 OnChange ();
2959                                 owner.Redraw (true);
2960                                 return value;
2961                         }
2962
2963                         public virtual ListViewItem Add (string text)
2964                         {
2965                                 ListViewItem item = new ListViewItem (text);
2966                                 return this.Add (item);
2967                         }
2968
2969                         public virtual ListViewItem Add (string text, int imageIndex)
2970                         {
2971                                 ListViewItem item = new ListViewItem (text, imageIndex);
2972                                 return this.Add (item);
2973                         }
2974
2975                         public void AddRange (ListViewItem [] values)
2976                         {
2977                                 list.Clear ();
2978
2979                                 foreach (ListViewItem item in values) {
2980                                         item.Owner = owner;
2981                                         list.Add (item);
2982                                 }
2983
2984                                 owner.Sort (false);
2985                                 OnChange ();
2986                                 owner.Redraw (true);
2987                         }
2988
2989                         public virtual void Clear ()
2990                         {
2991                                 owner.SetFocusedItem (null);
2992                                 owner.h_scroll.Value = owner.v_scroll.Value = 0;
2993                                 list.Clear ();
2994                                 OnChange ();
2995                                 owner.Redraw (true);
2996                         }
2997
2998                         public bool Contains (ListViewItem item)
2999                         {
3000                                 return list.Contains (item);
3001                         }
3002
3003                         public void CopyTo (Array dest, int index)
3004                         {
3005                                 list.CopyTo (dest, index);
3006                         }
3007
3008                         public IEnumerator GetEnumerator ()
3009                         {
3010                                 return list.GetEnumerator ();
3011                         }
3012
3013                         int IList.Add (object item)
3014                         {
3015                                 int result;
3016                                 ListViewItem li;
3017
3018                                 if (item is ListViewItem) {
3019                                         li = (ListViewItem) item;
3020                                         if (list.Contains (li))
3021                                                 throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
3022                                 }
3023                                 else
3024                                         li = new ListViewItem (item.ToString ());
3025
3026                                 li.Owner = owner;
3027                                 result = list.Add (li);
3028                                 OnChange ();
3029                                 owner.Redraw (true);
3030
3031                                 return result;
3032                         }
3033
3034                         bool IList.Contains (object item)
3035                         {
3036                                 return list.Contains (item);
3037                         }
3038
3039                         int IList.IndexOf (object item)
3040                         {
3041                                 return list.IndexOf (item);
3042                         }
3043
3044                         void IList.Insert (int index, object item)
3045                         {
3046                                 if (item is ListViewItem)
3047                                         this.Insert (index, (ListViewItem) item);
3048                                 else
3049                                         this.Insert (index, item.ToString ());
3050                         }
3051
3052                         void IList.Remove (object item)
3053                         {
3054                                 Remove ((ListViewItem) item);
3055                         }
3056
3057                         public int IndexOf (ListViewItem item)
3058                         {
3059                                 return list.IndexOf (item);
3060                         }
3061
3062                         public ListViewItem Insert (int index, ListViewItem item)
3063                         {
3064                                 if (index < 0 || index > list.Count)
3065                                         throw new ArgumentOutOfRangeException ("index");
3066
3067                                 if (list.Contains (item))
3068                                         throw new ArgumentException ("An item cannot be added more than once. To add an item again, you need to clone it.", "item");
3069
3070                                 item.Owner = owner;
3071                                 list.Insert (index, item);
3072                                 OnChange ();
3073                                 owner.Redraw (true);
3074                                 return item;
3075                         }
3076
3077                         public ListViewItem Insert (int index, string text)
3078                         {
3079                                 return this.Insert (index, new ListViewItem (text));
3080                         }
3081
3082                         public ListViewItem Insert (int index, string text, int imageIndex)
3083                         {
3084                                 return this.Insert (index, new ListViewItem (text, imageIndex));
3085                         }
3086
3087                         public virtual void Remove (ListViewItem item)
3088                         {
3089                                 if (!list.Contains (item))
3090                                         return;
3091                                         
3092                                 bool selection_changed = owner.SelectedItems.Contains (item);
3093                                 list.Remove (item);
3094                                 OnChange ();
3095                                 owner.Redraw (true);                            
3096                                 if (selection_changed)
3097                                         owner.OnSelectedIndexChanged (EventArgs.Empty);
3098                         }
3099
3100                         public virtual void RemoveAt (int index)
3101                         {
3102                                 if (index < 0 || index >= Count)
3103                                         throw new ArgumentOutOfRangeException ("index");
3104                                 bool selection_changed = owner.SelectedIndices.Contains (index);
3105                                 list.RemoveAt (index);
3106                                 OnChange ();
3107                                 owner.Redraw (false);
3108                                 if (selection_changed)
3109                                         owner.OnSelectedIndexChanged (EventArgs.Empty);
3110                         }
3111                         #endregion      // Public Methods
3112
3113                         internal event CollectionChangedHandler Changed;
3114
3115                         internal void Sort (IComparer comparer)
3116                         {
3117                                 list.Sort (comparer);
3118                                 OnChange ();
3119                         }
3120
3121                         internal void OnChange ()
3122                         {
3123                                 if (Changed != null)
3124                                         Changed ();
3125                         }
3126                 }       // ListViewItemCollection
3127
3128                 public class SelectedIndexCollection : IList, ICollection, IEnumerable
3129                 {
3130                         private readonly ListView owner;
3131
3132                         #region Public Constructor
3133                         public SelectedIndexCollection (ListView owner)
3134                         {
3135                                 this.owner = owner;
3136                         }
3137                         #endregion      // Public Constructor
3138
3139                         #region Public Properties
3140                         [Browsable (false)]
3141                         public int Count {
3142                                 get {
3143                                         return owner.SelectedItems.Count;
3144                                 }
3145                         }
3146
3147                         public bool IsReadOnly {
3148                                 get { return true; }
3149                         }
3150
3151                         public int this [int index] {
3152                                 get {
3153                                         int [] indices = GetIndices ();
3154                                         if (index < 0 || index >= indices.Length)
3155                                                 throw new ArgumentOutOfRangeException ("index");
3156                                         return indices [index];
3157                                 }
3158                         }
3159
3160                         bool ICollection.IsSynchronized {
3161                                 get { return false; }
3162                         }
3163
3164                         object ICollection.SyncRoot {
3165                                 get { return this; }
3166                         }
3167
3168                         bool IList.IsFixedSize {
3169                                 get { return true; }
3170                         }
3171
3172                         object IList.this [int index] {
3173                                 get { return this [index]; }
3174                                 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3175                         }
3176                         #endregion      // Public Properties
3177
3178                         #region Public Methods
3179                         public bool Contains (int selectedIndex)
3180                         {
3181                                 int [] indices = GetIndices ();
3182                                 for (int i = 0; i < indices.Length; i++) {
3183                                         if (indices [i] == selectedIndex)
3184                                                 return true;
3185                                 }
3186                                 return false;
3187                         }
3188
3189                         public void CopyTo (Array dest, int index)
3190                         {
3191                                 int [] indices = GetIndices ();
3192                                 Array.Copy (indices, 0, dest, index, indices.Length);
3193                         }
3194
3195                         public IEnumerator GetEnumerator ()
3196                         {
3197                                 int [] indices = GetIndices ();
3198                                 return indices.GetEnumerator ();
3199                         }
3200
3201                         int IList.Add (object value)
3202                         {
3203                                 throw new NotSupportedException ("Add operation is not supported.");
3204                         }
3205
3206                         void IList.Clear ()
3207                         {
3208                                 throw new NotSupportedException ("Clear operation is not supported.");
3209                         }
3210
3211                         bool IList.Contains (object selectedIndex)
3212                         {
3213                                 if (!(selectedIndex is int))
3214                                         return false;
3215                                 return Contains ((int) selectedIndex);
3216                         }
3217
3218                         int IList.IndexOf (object selectedIndex)
3219                         {
3220                                 if (!(selectedIndex is int))
3221                                         return -1;
3222                                 return IndexOf ((int) selectedIndex);
3223                         }
3224
3225                         void IList.Insert (int index, object value)
3226                         {
3227                                 throw new NotSupportedException ("Insert operation is not supported.");
3228                         }
3229
3230                         void IList.Remove (object value)
3231                         {
3232                                 throw new NotSupportedException ("Remove operation is not supported.");
3233                         }
3234
3235                         void IList.RemoveAt (int index)
3236                         {
3237                                 throw new NotSupportedException ("RemoveAt operation is not supported.");
3238                         }
3239
3240                         public int IndexOf (int selectedIndex)
3241                         {
3242                                 int [] indices = GetIndices ();
3243                                 for (int i = 0; i < indices.Length; i++) {
3244                                         if (indices [i] == selectedIndex)
3245                                                 return i;
3246                                 }
3247                                 return -1;
3248                         }
3249                         #endregion      // Public Methods
3250
3251                         private int [] GetIndices ()
3252                         {
3253                                 ArrayList selected_items = owner.SelectedItems.List;
3254                                 int [] indices = new int [selected_items.Count];
3255                                 for (int i = 0; i < selected_items.Count; i++) {
3256                                         ListViewItem item = (ListViewItem) selected_items [i];
3257                                         indices [i] = item.Index;
3258                                 }
3259                                 return indices;
3260                         }
3261                 }       // SelectedIndexCollection
3262
3263                 public class SelectedListViewItemCollection : IList, ICollection, IEnumerable
3264                 {
3265                         private readonly ListView owner;
3266                         private ArrayList list;
3267
3268                         #region Public Constructor
3269                         public SelectedListViewItemCollection (ListView owner)
3270                         {
3271                                 this.owner = owner;
3272                                 this.owner.Items.Changed += new CollectionChangedHandler (
3273                                         ItemsCollection_Changed);
3274                         }
3275                         #endregion      // Public Constructor
3276
3277                         #region Public Properties
3278                         [Browsable (false)]
3279                         public int Count {
3280                                 get {
3281                                         if (!owner.IsHandleCreated)
3282                                                 return 0;
3283                                         return List.Count;
3284                                 }
3285                         }
3286
3287                         public bool IsReadOnly {
3288                                 get { return true; }
3289                         }
3290
3291                         public ListViewItem this [int index] {
3292                                 get {
3293                                         ArrayList selected_items = List;
3294                                         if (!owner.IsHandleCreated || index < 0 || index >= selected_items.Count)
3295                                                 throw new ArgumentOutOfRangeException ("index");
3296                                         return (ListViewItem) selected_items [index];
3297                                 }
3298                         }
3299
3300                         bool ICollection.IsSynchronized {
3301                                 get { return false; }
3302                         }
3303
3304                         object ICollection.SyncRoot {
3305                                 get { return this; }
3306                         }
3307
3308                         bool IList.IsFixedSize {
3309                                 get { return true; }
3310                         }
3311
3312                         object IList.this [int index] {
3313                                 get { return this [index]; }
3314                                 set { throw new NotSupportedException ("SetItem operation is not supported."); }
3315                         }
3316                         #endregion      // Public Properties
3317
3318                         #region Public Methods
3319                         public void Clear ()
3320                         {
3321                                 if (!owner.IsHandleCreated)
3322                                         return;
3323
3324                                 foreach (ListViewItem item in List)
3325                                         item.Selected = false;
3326                         }
3327
3328                         public bool Contains (ListViewItem item)
3329                         {
3330                                 if (!owner.IsHandleCreated)
3331                                         return false;
3332                                 return List.Contains (item);
3333                         }
3334
3335                         public void CopyTo (Array dest, int index)
3336                         {
3337                                 if (!owner.IsHandleCreated)
3338                                         return;
3339                                 List.CopyTo (dest, index);
3340                         }
3341
3342                         public IEnumerator GetEnumerator ()
3343                         {
3344                                 if (!owner.IsHandleCreated)
3345                                         return (new ListViewItem [0]).GetEnumerator ();
3346                                 return List.GetEnumerator ();
3347                         }
3348
3349                         int IList.Add (object value)
3350                         {
3351                                 throw new NotSupportedException ("Add operation is not supported.");
3352                         }
3353
3354                         bool IList.Contains (object item)
3355                         {
3356                                 if (!(item is ListViewItem))
3357                                         return false;
3358                                 return Contains ((ListViewItem) item);
3359                         }
3360
3361                         int IList.IndexOf (object item)
3362                         {
3363                                 if (!(item is ListViewItem))
3364                                         return -1;
3365                                 return IndexOf ((ListViewItem) item);
3366                         }
3367
3368                         void IList.Insert (int index, object value)
3369                         {
3370                                 throw new NotSupportedException ("Insert operation is not supported.");
3371                         }
3372
3373                         void IList.Remove (object value)
3374                         {
3375                                 throw new NotSupportedException ("Remove operation is not supported.");
3376                         }
3377
3378                         void IList.RemoveAt (int index)
3379                         {
3380                                 throw new NotSupportedException ("RemoveAt operation is not supported.");
3381                         }
3382
3383                         public int IndexOf (ListViewItem item)
3384                         {
3385                                 if (!owner.IsHandleCreated)
3386                                         return -1;
3387                                 return List.IndexOf (item);
3388                         }
3389                         #endregion      // Public Methods
3390
3391                         internal ArrayList List {
3392                                 get {
3393                                         if (list == null) {
3394                                                 list = new ArrayList ();
3395                                                 foreach (ListViewItem item in owner.Items) {
3396                                                         if (item.Selected)
3397                                                                 list.Add (item);
3398                                                 }
3399                                         }
3400                                         return list;
3401                                 }
3402                         }
3403
3404                         internal void Reset ()
3405                         {
3406                                 // force re-population of list
3407                                 list = null;
3408                         }
3409
3410                         private void ItemsCollection_Changed ()
3411                         {
3412                                 Reset ();
3413                         }
3414                 }       // SelectedListViewItemCollection
3415
3416                 internal delegate void CollectionChangedHandler ();
3417
3418                 #endregion // Subclasses
3419         }
3420 }