2008-06-11 Jonathan Pobst <monkey@jpobst.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ListBox.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-2006 Novell, Inc.
21 //
22 // Authors:
23 //      Jordi Mas i Hernandez, jordi@ximian.com
24 //      Mike Kestner  <mkestner@novell.com>
25 //
26
27 // COMPLETE
28
29 using System;
30 using System.Drawing;
31 using System.Collections;
32 using System.ComponentModel;
33 using System.ComponentModel.Design;
34 using System.ComponentModel.Design.Serialization;
35 using System.Globalization;
36 using System.Reflection;
37 using System.Runtime.InteropServices;
38
39 #if NET_2_0
40 using System.Collections.Generic;
41 #endif
42
43 namespace System.Windows.Forms
44 {
45         [DefaultProperty("Items")]
46         [DefaultEvent("SelectedIndexChanged")]
47         [Designer ("System.Windows.Forms.Design.ListBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
48 #if NET_2_0
49         [DefaultBindingProperty ("SelectedValue")]
50         [ClassInterface (ClassInterfaceType.AutoDispatch)]
51         [ComVisible (true)]
52 #endif
53         public class ListBox : ListControl
54         {
55                 public const int DefaultItemHeight = 13;
56                 public const int NoMatches = -1;
57                 
58                 internal enum ItemNavigation
59                 {
60                         First,
61                         Last,
62                         Next,
63                         Previous,
64                         NextPage,
65                         PreviousPage,
66                         PreviousColumn,
67                         NextColumn
68                 }
69                 
70                 Hashtable item_heights;
71                 private int item_height = -1;
72                 private int column_width = 0;
73                 private int requested_height;
74                 private DrawMode draw_mode = DrawMode.Normal;
75                 private int horizontal_extent = 0;
76                 private bool horizontal_scrollbar = false;
77                 private bool integral_height = true;
78                 private bool multicolumn = false;
79                 private bool scroll_always_visible = false;
80                 private SelectedIndexCollection selected_indices;
81                 private SelectedObjectCollection selected_items;
82                 private SelectionMode selection_mode = SelectionMode.One;
83                 private bool sorted = false;
84                 private bool use_tabstops = true;
85                 private int column_width_internal = 120;
86                 private ImplicitVScrollBar vscrollbar;
87                 private ImplicitHScrollBar hscrollbar;
88                 private int hbar_offset;
89                 private bool suspend_layout;
90                 private bool ctrl_pressed = false;
91                 private bool shift_pressed = false;
92                 private bool explicit_item_height = false;
93                 private int top_index = 0;
94                 private int last_visible_index = 0;
95                 private Rectangle items_area;
96                 private int focused_item = -1;
97                 private ObjectCollection items;
98 #if NET_2_0
99                 private IntegerCollection custom_tab_offsets;
100                 private Padding padding;
101                 private bool use_custom_tab_offsets;
102 #endif
103
104                 public ListBox ()
105                 {
106                         items = CreateItemCollection ();
107                         selected_indices = new SelectedIndexCollection (this);
108                         selected_items = new SelectedObjectCollection (this);
109
110                         requested_height = bounds.Height;
111                         InternalBorderStyle = BorderStyle.Fixed3D;
112                         BackColor = ThemeEngine.Current.ColorWindow;
113
114                         /* Vertical scrollbar */
115                         vscrollbar = new ImplicitVScrollBar ();
116                         vscrollbar.Minimum = 0;
117                         vscrollbar.SmallChange = 1;
118                         vscrollbar.LargeChange = 1;
119                         vscrollbar.Maximum = 0;
120                         vscrollbar.ValueChanged += new EventHandler (VerticalScrollEvent);
121                         vscrollbar.Visible = false;
122
123                         /* Horizontal scrollbar */
124                         hscrollbar = new ImplicitHScrollBar ();
125                         hscrollbar.Minimum = 0;
126                         hscrollbar.SmallChange = 1;
127                         hscrollbar.LargeChange = 1;
128                         hscrollbar.Maximum = 0;
129                         hscrollbar.Visible = false;
130                         hscrollbar.ValueChanged += new EventHandler (HorizontalScrollEvent);
131
132                         Controls.AddImplicit (vscrollbar);
133                         Controls.AddImplicit (hscrollbar);
134
135                         /* Events */
136                         MouseDown += new MouseEventHandler (OnMouseDownLB);
137                         MouseMove += new MouseEventHandler (OnMouseMoveLB);
138                         MouseUp += new MouseEventHandler (OnMouseUpLB);
139                         MouseWheel += new MouseEventHandler (OnMouseWheelLB);
140                         KeyDown += new KeyEventHandler (OnKeyDownLB);
141                         KeyUp += new KeyEventHandler (OnKeyUpLB);
142                         GotFocus += new EventHandler (OnGotFocus);
143                         LostFocus += new EventHandler (OnLostFocus);
144                         
145                         SetStyle (ControlStyles.UserPaint, false);
146
147 #if NET_2_0
148                         custom_tab_offsets = new IntegerCollection (this);
149 #endif
150                 }
151
152                 #region Events
153                 static object DrawItemEvent = new object ();
154                 static object MeasureItemEvent = new object ();
155                 static object SelectedIndexChangedEvent = new object ();
156
157                 [Browsable (false)]
158                 [EditorBrowsable (EditorBrowsableState.Never)]
159                 public new event EventHandler BackgroundImageChanged {
160                         add { base.BackgroundImageChanged += value; }
161                         remove { base.BackgroundImageChanged -= value; }
162                 }
163
164 #if NET_2_0
165                 [Browsable (false)]
166                 [EditorBrowsable (EditorBrowsableState.Never)]
167                 public new event EventHandler BackgroundImageLayoutChanged {
168                         add { base.BackgroundImageLayoutChanged += value; }
169                         remove { base.BackgroundImageLayoutChanged -= value; }
170                 }
171
172                 [Browsable (true)]
173                 [EditorBrowsable (EditorBrowsableState.Always)]
174 #else
175                 [Browsable (false)]
176                 [EditorBrowsable (EditorBrowsableState.Advanced)]
177 #endif
178                 public new event EventHandler Click {
179                         add { base.Click += value; }
180                         remove { base.Click -= value; }
181                 }
182
183                 public event DrawItemEventHandler DrawItem {
184                         add { Events.AddHandler (DrawItemEvent, value); }
185                         remove { Events.RemoveHandler (DrawItemEvent, value); }
186                 }
187
188                 public event MeasureItemEventHandler MeasureItem {
189                         add { Events.AddHandler (MeasureItemEvent, value); }
190                         remove { Events.RemoveHandler (MeasureItemEvent, value); }
191                 }
192
193 #if NET_2_0
194                 [Browsable (true)]
195                 [EditorBrowsable (EditorBrowsableState.Always)]
196                 public new event MouseEventHandler MouseClick {
197                         add { base.MouseClick += value; }
198                         remove { base.MouseClick -= value; }
199                 }
200
201                 [Browsable (false)]
202                 [EditorBrowsable (EditorBrowsableState.Never)]
203                 public new event EventHandler PaddingChanged {
204                         add { base.PaddingChanged += value; }
205                         remove { base.PaddingChanged -= value; }
206                 }
207 #endif
208
209                 [Browsable (false)]
210                 [EditorBrowsable (EditorBrowsableState.Never)]
211                 public new event PaintEventHandler Paint {
212                         add { base.Paint += value; }
213                         remove { base.Paint -= value; }
214                 }
215
216                 public event EventHandler SelectedIndexChanged {
217                         add { Events.AddHandler (SelectedIndexChangedEvent, value); }
218                         remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
219                 }
220
221                 [Browsable (false)]
222                 [EditorBrowsable (EditorBrowsableState.Advanced)]
223                 public new event EventHandler TextChanged {
224                         add { base.TextChanged += value; }
225                         remove { base.TextChanged -= value; }
226                 }
227                 #endregion // Events
228
229                 #region Public Properties
230                 public override Color BackColor {
231                         get { return base.BackColor; }
232                         set {
233                                 if (base.BackColor == value)
234                                         return;
235
236                                 base.BackColor = value;
237                                 base.Refresh ();        // Careful. Calling the base method is not the same that calling 
238                         }                               // the overriden one that refresh also all the items
239                 }
240
241                 [Browsable (false)]
242                 [EditorBrowsable (EditorBrowsableState.Never)]
243                 public override Image BackgroundImage {
244                         get { return base.BackgroundImage; }
245                         set { 
246                                 base.BackgroundImage = value;
247                                 base.Refresh ();
248                         }
249                 }
250
251 #if NET_2_0
252                 [Browsable (false)]
253                 [EditorBrowsable (EditorBrowsableState.Never)]
254                 public override ImageLayout BackgroundImageLayout {
255                         get { return base.BackgroundImageLayout; }
256                         set { base.BackgroundImageLayout = value; }
257                 }
258 #endif
259
260                 [DefaultValue (BorderStyle.Fixed3D)]
261                 [DispId(-504)]
262                 public BorderStyle BorderStyle {
263                         get { return InternalBorderStyle; }
264                         set { 
265                                 InternalBorderStyle = value; 
266                                 UpdateListBoxBounds ();
267                         }
268                 }
269
270                 [DefaultValue (0)]
271                 [Localizable (true)]
272                 public int ColumnWidth {
273                         get { return column_width; }
274                         set {
275                                 if (value < 0)
276                                         throw new ArgumentException ("A value less than zero is assigned to the property.");
277
278                                 column_width = value;
279
280                                 if (value == 0)
281                                         ColumnWidthInternal = 120;
282                                 else
283                                         ColumnWidthInternal = value;
284
285                                 base.Refresh ();
286                         }
287                 }
288
289                 protected override CreateParams CreateParams {
290                         get { return base.CreateParams;}
291                 }
292
293 #if NET_2_0
294                 [Browsable (false)]
295                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
296                 public IntegerCollection CustomTabOffsets {
297                         get { return custom_tab_offsets; }
298                 }
299 #endif
300
301                 protected override Size DefaultSize {
302                         get { return new Size (120, 96); }
303                 }
304
305                 [RefreshProperties(RefreshProperties.Repaint)]
306                 [DefaultValue (DrawMode.Normal)]
307                 public virtual DrawMode DrawMode {
308                         get { return draw_mode; }
309                         set {
310                                 if (!Enum.IsDefined (typeof (DrawMode), value))
311                                         throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for DrawMode", value));
312                                         
313                                 if (value == DrawMode.OwnerDrawVariable && multicolumn == true)
314                                         throw new ArgumentException ("Cannot have variable height and multicolumn");
315
316                                 if (draw_mode == value)
317                                         return;
318
319                                 draw_mode = value;
320
321                                 if (draw_mode == DrawMode.OwnerDrawVariable)
322                                         item_heights = new Hashtable ();
323                                 else
324                                         item_heights = null;
325
326                                 base.Refresh ();
327                         }
328                 }
329
330 #if NET_2_0
331                 public override Font Font {
332                         get { return base.Font; }
333                         set { base.Font = value; }
334                 }
335 #endif
336
337                 public override Color ForeColor {
338                         get { return base.ForeColor; }
339                         set {
340                                 if (base.ForeColor == value)
341                                         return;
342
343                                 base.ForeColor = value;
344                                 base.Refresh ();
345                         }
346                 }
347
348                 [DefaultValue (0)]
349                 [Localizable (true)]
350                 public int HorizontalExtent {
351                         get { return horizontal_extent; }
352                         set {
353                                 if (horizontal_extent == value)
354                                         return;
355
356                                 horizontal_extent = value;
357                                 base.Refresh ();
358                         }
359                 }
360
361                 [DefaultValue (false)]
362                 [Localizable (true)]
363                 public bool HorizontalScrollbar {
364                         get { return horizontal_scrollbar; }
365                         set {
366                                 if (horizontal_scrollbar == value)
367                                         return;
368
369                                 horizontal_scrollbar = value;
370                                 UpdateScrollBars ();
371                                 base.Refresh ();
372                         }
373                 }
374
375                 [DefaultValue (true)]
376                 [Localizable (true)]
377                 [RefreshProperties(RefreshProperties.Repaint)]
378                 public bool IntegralHeight {
379                         get { return integral_height; }
380                         set {
381                                 if (integral_height == value)
382                                         return;
383
384                                 integral_height = value;
385                                 UpdateListBoxBounds ();
386                         }
387                 }
388
389                 [DefaultValue (13)]
390                 [Localizable (true)]
391                 [RefreshProperties(RefreshProperties.Repaint)]
392                 public virtual int ItemHeight {
393                         get {
394                                 if (item_height == -1) {
395                                         SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
396                                         item_height = (int) sz.Height;
397                                 }
398                                 return item_height;
399                         }
400                         set {
401                                 if (value > 255)
402                                         throw new ArgumentOutOfRangeException ("The ItemHeight property was set beyond 255 pixels");
403
404                                 explicit_item_height = true;
405                                 if (item_height == value)
406                                         return;
407
408                                 item_height = value;
409                                 if (IntegralHeight)
410                                         UpdateListBoxBounds ();
411                                 LayoutListBox ();
412                         }
413                 }
414
415                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
416                 [Localizable (true)]
417                 [Editor ("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
418 #if NET_2_0
419                 [MergableProperty (false)]
420 #endif
421                 public ObjectCollection Items {
422                         get { return items; }
423                 }
424
425                 [DefaultValue (false)]
426                 public bool MultiColumn {
427                         get { return multicolumn; }
428                         set {
429                                 if (multicolumn == value)
430                                         return;
431
432                                 if (value == true && DrawMode == DrawMode.OwnerDrawVariable)
433                                         throw new ArgumentException ("A multicolumn ListBox cannot have a variable-sized height.");
434                                         
435                                 multicolumn = value;
436                                 LayoutListBox ();
437                                 Invalidate ();
438                         }
439                 }
440
441 #if NET_2_0
442                 [Browsable (false)]
443                 [EditorBrowsable (EditorBrowsableState.Never)]
444                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
445                 public new Padding Padding {
446                         get { return padding; }
447                         set { padding = value; }
448                 }
449 #endif
450
451                 [Browsable (false)]
452                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
453                 [EditorBrowsable (EditorBrowsableState.Advanced)]
454                 public int PreferredHeight {
455                         get {
456                                 int itemsHeight = 0;
457                                 if (draw_mode == DrawMode.Normal)
458                                         itemsHeight = FontHeight * items.Count;
459                                 else if (draw_mode == DrawMode.OwnerDrawFixed)
460                                         itemsHeight = ItemHeight * items.Count;
461                                 else if (draw_mode == DrawMode.OwnerDrawVariable) {
462                                         for (int i = 0; i < items.Count; i++)
463                                                 itemsHeight += (int) item_heights [Items [i]];
464                                 }
465                                 
466                                 return itemsHeight;
467                         }
468                 }
469
470                 public override RightToLeft RightToLeft {
471                         get { return base.RightToLeft; }
472                         set {
473                                 base.RightToLeft = value;
474                                 if (base.RightToLeft == RightToLeft.Yes)
475                                         StringFormat.Alignment = StringAlignment.Far;
476                                 else
477                                         StringFormat.Alignment = StringAlignment.Near;
478                                 base.Refresh ();
479                         }
480                 }
481
482                 // Only affects the Vertical ScrollBar
483                 [DefaultValue (false)]
484                 [Localizable (true)]
485                 public bool ScrollAlwaysVisible {
486                         get { return scroll_always_visible; }
487                         set {
488                                 if (scroll_always_visible == value)
489                                         return;
490
491                                 scroll_always_visible = value;
492                                 UpdateScrollBars ();
493                         }
494                 }
495
496                 [Bindable(true)]
497                 [Browsable (false)]
498                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
499                 public override int SelectedIndex {
500                         get { 
501                                 if (selected_indices == null)
502                                         return -1;
503                                         
504                                 return selected_indices.Count > 0 ? selected_indices [0] : -1;
505                         }
506                         set {
507                                 if (value < -1 || value >= Items.Count)
508                                         throw new ArgumentOutOfRangeException ("Index of out range");
509
510                                 if (SelectionMode == SelectionMode.None)
511                                         throw new ArgumentException ("cannot call this method if SelectionMode is SelectionMode.None");
512
513                                 if (value == -1)
514                                         selected_indices.Clear ();
515                                 else
516                                         selected_indices.Add (value);
517                         }
518                 }
519
520                 [Browsable (false)]
521                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
522                 public SelectedIndexCollection SelectedIndices {
523                         get { return selected_indices; }
524                 }
525
526                 [Bindable(true)]
527                 [Browsable (false)]
528                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
529                 public object SelectedItem {
530                         get {
531                                 if (SelectedItems.Count > 0)
532                                         return SelectedItems[0];
533                                 else
534                                         return null;
535                         }
536                         set {
537                                 if (value != null && !Items.Contains (value))
538                                         return; // FIXME: this is probably an exception
539                                         
540                                 SelectedIndex = value == null ? - 1 : Items.IndexOf (value);
541                         }
542                 }
543
544                 [Browsable (false)]
545                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
546                 public SelectedObjectCollection SelectedItems {
547                         get {return selected_items;}
548                 }
549
550                 [DefaultValue (SelectionMode.One)]
551                 public virtual SelectionMode SelectionMode {
552                         get { return selection_mode; }
553                         set {
554                                 if (!Enum.IsDefined (typeof (SelectionMode), value))
555                                         throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for SelectionMode", value));
556
557                                 if (selection_mode == value)
558                                         return;
559                                         
560                                 selection_mode = value;
561                                         
562                                 switch (selection_mode) {
563                                 case SelectionMode.None: 
564                                         SelectedIndices.Clear ();
565                                         break;
566
567                                 case SelectionMode.One:
568                                         // FIXME: Probably this can be improved
569                                         ArrayList old_selection = (ArrayList) SelectedIndices.List.Clone ();
570                                         for (int i = 1; i < old_selection.Count; i++)
571                                                 SelectedIndices.Remove ((int)old_selection [i]);
572                                         break;
573
574                                 default:
575                                         break;
576                                 }
577                         }
578                 }
579
580                 [DefaultValue (false)]
581                 public bool Sorted {
582                         get { return sorted; }
583                         set {
584                                 if (sorted == value)
585                                         return;
586
587                                 sorted = value;
588                                 if (sorted)
589                                         Sort ();
590                         }
591                 }
592
593                 [Bindable (false)]
594                 [Browsable (false)]
595                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
596                 [EditorBrowsable (EditorBrowsableState.Advanced)]
597                 public override string Text {
598                         get {
599                                 if (SelectionMode != SelectionMode.None && SelectedIndex != -1)
600                                         return GetItemText (SelectedItem);
601
602                                 return base.Text;
603                         }
604                         set {
605
606                                 base.Text = value;
607
608                                 if (SelectionMode == SelectionMode.None)
609                                         return;
610
611                                 int index;
612
613                                 index = FindStringExact (value);
614
615                                 if (index == -1)
616                                         return;
617
618                                 SelectedIndex = index;
619                         }
620                 }
621
622                 [Browsable (false)]
623                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
624                 public int TopIndex {
625                         get { return top_index; }
626                         set {
627                                 if (value == top_index)
628                                         return;
629
630                                 if (value < 0 || value >= Items.Count)
631                                         return;
632
633                                 top_index = value;
634                                 UpdateTopItem ();
635                                 base.Refresh ();
636                         }
637                 }
638
639 #if NET_2_0
640                 [Browsable (false)]
641                 [DefaultValue (false)]
642                 public bool UseCustomTabOffsets {
643                         get { return use_custom_tab_offsets; }
644                         set { 
645                                 if (use_custom_tab_offsets != value) {
646                                         use_custom_tab_offsets = value;
647                                         CalculateTabStops ();
648                                 }
649                          }
650                 }
651 #endif
652                 [DefaultValue (true)]
653                 public bool UseTabStops {
654                         get { return use_tabstops; }
655                         set {
656                                 if (use_tabstops == value)
657                                         return;
658
659                                 use_tabstops = value;
660                                 CalculateTabStops ();
661                         }
662                 }
663
664 #if NET_2_0
665                 protected override bool AllowSelection {
666                         get {
667                                 return SelectionMode != SelectionMode.None;
668                         }
669                 }
670 #endif
671
672                 #endregion Public Properties
673
674                 #region Private Properties
675
676                 private int ColumnWidthInternal {
677                         get { return column_width_internal; }
678                         set { column_width_internal = value; }
679                 }
680
681                 private int row_count = 1;
682                 private int RowCount {
683                         get {
684                                 return MultiColumn ? row_count : Items.Count;
685                         }
686                 }
687
688                 #endregion Private Properties
689
690                 #region Public Methods
691 #if NET_2_0
692                 [Obsolete ("this method has been deprecated")]
693 #endif
694                 protected virtual void AddItemsCore (object[] value)
695                 {
696                         Items.AddRange (value);
697                 }
698
699                 public void BeginUpdate ()
700                 {
701                         suspend_layout = true;
702                 }
703
704                 public void ClearSelected ()
705                 {
706                         selected_indices.Clear ();
707                 }
708
709                 protected virtual ObjectCollection CreateItemCollection ()
710                 {
711                         return new ObjectCollection (this);
712                 }
713
714                 public void EndUpdate ()
715                 {
716                         suspend_layout = false;
717                         LayoutListBox ();
718                         base.Refresh ();
719                 }
720
721                 public int FindString (String s)
722                 {
723                         return FindString (s, -1);
724                 }
725
726                 public int FindString (string s,  int startIndex)
727                 {
728                         if (Items.Count == 0)
729                                 return -1; // No exception throwing if empty
730
731                         if (startIndex < -1 || startIndex >= Items.Count)
732                                 throw new ArgumentOutOfRangeException ("Index of out range");
733
734                         startIndex = (startIndex == Items.Count - 1) ? 0 : startIndex + 1;
735
736                         int i = startIndex;
737                         while (true) {
738                                 string text = GetItemText (Items [i]);
739                                 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (text, s,
740                                                 CompareOptions.IgnoreCase))
741                                         return i;
742
743                                 i = (i == Items.Count - 1) ? 0 : i + 1;
744                                 if (i == startIndex)
745                                         break;
746                         }
747
748                         return NoMatches;
749                 }
750
751                 public int FindStringExact (string s)
752                 {
753                         return FindStringExact (s, -1);
754                 }
755
756                 public int FindStringExact (string s,  int startIndex)
757                 {
758                         if (Items.Count == 0)
759                                 return -1; // No exception throwing if empty
760
761                         if (startIndex < -1 || startIndex >= Items.Count)
762                                 throw new ArgumentOutOfRangeException ("Index of out range");
763
764                         startIndex = (startIndex + 1 == Items.Count) ? 0 : startIndex + 1;
765
766                         int i = startIndex;
767                         while (true) {
768                                 if (String.Compare (GetItemText (Items[i]), s, true) == 0)
769                                         return i;
770
771                                 i = (i + 1 == Items.Count) ? 0 : i + 1;
772                                 if (i == startIndex)
773                                         break;
774                         }
775
776                         return NoMatches;
777                 }
778
779                 public int GetItemHeight (int index)
780                 {
781                         if (index < 0 || index >= Items.Count)
782                                 throw new ArgumentOutOfRangeException ("Index of out range");
783                                 
784                         if (DrawMode == DrawMode.OwnerDrawVariable && IsHandleCreated == true) {
785                                 
786                                 object o = Items [index];
787                                 if (item_heights.Contains (o))
788                                         return (int) item_heights [o];
789                                 
790                                 MeasureItemEventArgs args = new MeasureItemEventArgs (DeviceContext, index, ItemHeight);
791                                 OnMeasureItem (args);
792                                 item_heights [o] = args.ItemHeight;
793                                 return args.ItemHeight;
794                         }
795
796                         return ItemHeight;
797                 }
798
799                 public Rectangle GetItemRectangle (int index)
800                 {
801                         if (index < 0 || index >= Items.Count)
802                                 throw new  ArgumentOutOfRangeException ("GetItemRectangle index out of range.");
803
804                         Rectangle rect = new Rectangle ();
805
806                         if (MultiColumn) {
807                                 int col = index / RowCount;
808                                 rect.Y = ((index - top_index) % RowCount) * ItemHeight;
809                                 rect.X = col * ColumnWidthInternal;
810                                 rect.Height = ItemHeight;
811                                 rect.Width = ColumnWidthInternal;
812                         } else {
813                                 rect.X = 0;
814                                 rect.Height = GetItemHeight (index);
815                                 rect.Width = items_area.Width;
816                                 
817                                 if (DrawMode == DrawMode.OwnerDrawVariable) {
818                                         rect.Y = 0;
819                                         if (index >= top_index) {
820                                                 for (int i = top_index; i < index; i++) {
821                                                         rect.Y += GetItemHeight (i);
822                                                 }
823                                         } else {
824                                                 for (int i = index; i < top_index; i++) {
825                                                         rect.Y -= GetItemHeight (i);
826                                                 }
827                                         }
828                                 } else {
829                                         rect.Y = ItemHeight * (index - top_index);      
830                                 }
831                         }
832
833                         if (this is CheckedListBox)
834                                 rect.Width += 15;
835                                 
836                         return rect;
837                 }
838
839 #if NET_2_0
840                 [EditorBrowsable (EditorBrowsableState.Advanced)]
841                 protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
842                 {
843                         bounds.Height = requested_height;
844
845                         return base.GetScaledBounds (bounds, factor, specified);
846                 }
847 #endif
848
849                 public bool GetSelected (int index)
850                 {
851                         if (index < 0 || index >= Items.Count)
852                                 throw new ArgumentOutOfRangeException ("Index of out range");
853
854                         return SelectedIndices.Contains (index);
855                 }
856
857                 public int IndexFromPoint (Point p)
858                 {
859                         return IndexFromPoint (p.X, p.Y);
860                 }
861
862                 // Only returns visible points
863                 public int IndexFromPoint (int x, int y)
864                 {
865
866                         if (Items.Count == 0) {
867                                 return -1;
868                         }
869
870                         for (int i = top_index; i <= last_visible_index; i++) {
871                                 if (GetItemRectangle (i).Contains (x,y) == true)
872                                         return i;
873                         }
874
875                         return -1;
876                 }
877
878                 protected override void OnChangeUICues (UICuesEventArgs e)
879                 {
880                         base.OnChangeUICues (e);
881                 }
882
883                 protected override void OnDataSourceChanged (EventArgs e)
884                 {
885                         base.OnDataSourceChanged (e);
886                         BindDataItems ();
887                         
888                         if (DataSource == null || DataManager == null) {
889                                 SelectedIndex = -1;
890                         } else {
891                                 SelectedIndex = DataManager.Position;
892                         }
893                 }
894
895                 protected override void OnDisplayMemberChanged (EventArgs e)
896                 {
897                         base.OnDisplayMemberChanged (e);
898
899                         if (DataManager == null || !IsHandleCreated)
900                                 return;
901
902                         BindDataItems ();
903                         base.Refresh ();
904                 }
905
906                 protected virtual void OnDrawItem (DrawItemEventArgs e)
907                 {                       
908                         switch (DrawMode) {
909                         case DrawMode.OwnerDrawFixed:
910                         case DrawMode.OwnerDrawVariable:
911                                 DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
912                                 if (eh != null)
913                                         eh (this, e);
914
915                                 break;
916
917                         default:
918                                 ThemeEngine.Current.DrawListBoxItem (this, e);
919                                 break;
920                         }
921                 }
922
923                 protected override void OnFontChanged (EventArgs e)
924                 {
925                         base.OnFontChanged (e);
926
927                         if (use_tabstops)
928                                 StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
929
930                         if (explicit_item_height) {
931                                 base.Refresh ();
932                         } else {
933                                 SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
934                                 item_height = (int) sz.Height;
935                                 if (IntegralHeight)
936                                         UpdateListBoxBounds ();
937                                 LayoutListBox ();
938                         }
939                 }
940
941                 protected override void OnHandleCreated (EventArgs e)
942                 {
943                         base.OnHandleCreated (e);
944
945                         if (IntegralHeight)
946                                 UpdateListBoxBounds ();
947
948                         LayoutListBox ();
949                         EnsureVisible (focused_item);
950                 }
951
952                 protected override void OnHandleDestroyed (EventArgs e)
953                 {
954                         base.OnHandleDestroyed (e);
955                 }
956
957                 protected virtual void OnMeasureItem (MeasureItemEventArgs e)
958                 {
959                         if (draw_mode != DrawMode.OwnerDrawVariable)
960                                 return;
961
962                         MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]);
963                         if (eh != null)
964                                 eh (this, e);
965                 }
966
967                 protected override void OnParentChanged (EventArgs e)
968                 {
969                         base.OnParentChanged (e);
970                 }
971
972                 protected override void OnResize (EventArgs e)
973                 {
974                         base.OnResize (e);
975                         if (canvas_size.IsEmpty || MultiColumn)
976                                 LayoutListBox ();
977                                 
978                         Invalidate ();
979                 }
980
981                 protected override void OnSelectedIndexChanged (EventArgs e)
982                 {
983                         base.OnSelectedIndexChanged (e);
984
985                         EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
986                         if (eh != null)
987                                 eh (this, e);
988                 }
989
990                 protected override void OnSelectedValueChanged (EventArgs e)
991                 {
992                         base.OnSelectedValueChanged (e);
993                 }
994
995                 public override void Refresh ()
996                 {
997                         if (draw_mode == DrawMode.OwnerDrawVariable)
998                                 item_heights.Clear ();
999                         
1000                         base.Refresh ();
1001                 }
1002
1003                 protected override void RefreshItem (int index)
1004                 {
1005                         if (index < 0 || index >= Items.Count)
1006                                 throw new ArgumentOutOfRangeException ("Index of out range");
1007                                 
1008                         if (draw_mode == DrawMode.OwnerDrawVariable)
1009                                 item_heights.Remove (Items [index]);
1010                 }
1011
1012 #if NET_2_0
1013                 protected override void RefreshItems ()
1014                 {
1015                         for (int i = 0; i < Items.Count; i++) {
1016                                 RefreshItem (i);
1017                         }
1018                 }
1019
1020                 public override void ResetBackColor ()
1021                 {
1022                         base.ResetBackColor ();
1023                 }
1024
1025                 public override void ResetForeColor ()
1026                 {
1027                         base.ResetForeColor ();
1028                 }
1029
1030                 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
1031                 {
1032                         base.ScaleControl (factor, specified);
1033                 }
1034 #endif
1035
1036                 private int SnapHeightToIntegral (int height)
1037                 {
1038                         int border;
1039
1040                         switch (border_style) {
1041                         case BorderStyle.Fixed3D:
1042                                 border = ThemeEngine.Current.Border3DSize.Height;
1043                                 break;
1044                         case BorderStyle.FixedSingle:
1045                                 border = ThemeEngine.Current.BorderSize.Height;
1046                                 break;
1047                         case BorderStyle.None:
1048                         default:
1049                                 border = 0;
1050                                 break;
1051                         }
1052
1053                         height -= (2 * border);
1054                         height -= height % ItemHeight;
1055                         height += (2 * border);
1056
1057                         return height;
1058                 }
1059                 
1060                 protected override void SetBoundsCore (int x,  int y, int width, int height, BoundsSpecified specified)
1061                 {
1062                         if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height)
1063                                 requested_height = height;
1064
1065                         if (IntegralHeight && IsHandleCreated)
1066                                 height = SnapHeightToIntegral (height);
1067
1068                         base.SetBoundsCore (x, y, width, height, specified);
1069                         UpdateScrollBars ();
1070                         last_visible_index = LastVisibleItem ();
1071                 }
1072
1073                 protected override void SetItemCore (int index,  object value)
1074                 {
1075                         if (index < 0 || index >= Items.Count)
1076                                 return;
1077
1078                         Items[index] = value;
1079                 }
1080
1081                 protected override void SetItemsCore (IList value)
1082                 {
1083                         BeginUpdate ();
1084                         try {
1085                                 Items.Clear ();
1086                                 Items.AddRange (value);
1087                         } finally {
1088                                 EndUpdate ();
1089                         }
1090                 }
1091
1092                 public void SetSelected (int index, bool value)
1093                 {
1094                         if (index < 0 || index >= Items.Count)
1095                                 throw new ArgumentOutOfRangeException ("Index of out range");
1096
1097                         if (SelectionMode == SelectionMode.None)
1098                                 throw new InvalidOperationException ();
1099
1100                         if (value)
1101                                 SelectedIndices.Add (index);
1102                         else
1103                                 SelectedIndices.Remove (index);
1104                 }
1105
1106                 protected virtual void Sort ()
1107                 {
1108                         Sort (true);
1109                 }
1110
1111                 //
1112                 // Sometimes we could need to Sort, and request a Refresh
1113                 // in a different place, to not have the painting done twice
1114                 //
1115                 void Sort (bool paint)
1116                 {
1117                         if (Items.Count == 0)
1118                                 return;
1119
1120                         Items.Sort ();
1121
1122                         if (paint)
1123                                 base.Refresh ();
1124                 }
1125
1126                 public override string ToString ()
1127                 {
1128                         return base.ToString ();
1129                 }
1130
1131                 protected virtual void WmReflectCommand (ref Message m)
1132                 {
1133                 }
1134
1135                 protected override void WndProc (ref Message m)
1136                 {
1137                         base.WndProc (ref m);
1138                 }
1139
1140                 #endregion Public Methods
1141
1142                 #region Private Methods
1143
1144                 private void CalculateTabStops ()
1145                 {
1146                         if (use_tabstops) {
1147 #if NET_2_0
1148                                 if (use_custom_tab_offsets) {
1149                                         float[] f = new float[custom_tab_offsets.Count];
1150                                         custom_tab_offsets.CopyTo (f, 0);
1151                                         StringFormat.SetTabStops (0, f);
1152                                 }
1153                                 else
1154 #endif
1155                                         StringFormat.SetTabStops (0, new float[] { (float)(Font.Height * 3.7) });
1156                         } else
1157                                 StringFormat.SetTabStops (0, new float[0]);
1158
1159                         this.Invalidate ();
1160                 }
1161
1162                 private Size canvas_size;
1163
1164                 private void LayoutListBox ()
1165                 {
1166                         if (!IsHandleCreated || suspend_layout)
1167                                 return;
1168
1169                         if (MultiColumn)
1170                                 LayoutMultiColumn ();
1171                         else
1172                                 LayoutSingleColumn ();
1173
1174                         UpdateScrollBars ();
1175                         last_visible_index = LastVisibleItem ();
1176                 }
1177
1178                 private void LayoutSingleColumn ()
1179                 {
1180                         int height, width;
1181
1182                         switch (DrawMode) {
1183                         case DrawMode.OwnerDrawVariable:
1184                                 height = 0;
1185                                 width = HorizontalExtent;
1186                                 for (int i = 0; i < Items.Count; i++) {
1187                                         height += GetItemHeight (i);
1188                                 }
1189                                 break;
1190
1191                         case DrawMode.OwnerDrawFixed:
1192                                 height = Items.Count * ItemHeight;
1193                                 width = HorizontalExtent;
1194                                 break;
1195
1196                         case DrawMode.Normal:
1197                         default:
1198                                 height = Items.Count * ItemHeight;
1199                                 width = 0;
1200                                 for (int i = 0; i < Items.Count; i++) {
1201                                         SizeF sz = TextRenderer.MeasureString (GetItemText (Items[i]), Font);
1202                                         int t = (int)sz.Width;
1203                                         
1204                                         if (this is CheckedListBox)
1205                                                 t += 15;
1206                                                 
1207                                         if (t > width)
1208                                                 width = t;
1209                                 }
1210                                 break;
1211                         }
1212
1213                         canvas_size = new Size (width, height);
1214                 }
1215
1216                 private void LayoutMultiColumn ()
1217                 {
1218                         int usable_height = ClientRectangle.Height - (ScrollAlwaysVisible ? hscrollbar.Height : 0);
1219                         row_count = Math.Max (1, usable_height / ItemHeight);
1220
1221                         int cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1222                         Size sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1223                         if (!ScrollAlwaysVisible && sz.Width > ClientRectangle.Width && row_count > 1) {
1224                                 usable_height = ClientRectangle.Height - hscrollbar.Height;
1225                                 row_count = Math.Max (1, usable_height / ItemHeight);
1226                                 cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1227                                 sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1228                         }
1229                         canvas_size = sz;
1230                 }
1231
1232                 internal void Draw (Rectangle clip, Graphics dc)
1233                 {
1234                         Theme theme = ThemeEngine.Current;
1235
1236                         if (hscrollbar.Visible && vscrollbar.Visible) {
1237                                 // Paint the dead space in the bottom right corner
1238                                 Rectangle rect = new Rectangle (hscrollbar.Right, vscrollbar.Bottom, vscrollbar.Width, hscrollbar.Height);
1239                                 if (rect.IntersectsWith (clip))
1240                                         dc.FillRectangle (theme.ResPool.GetSolidBrush (theme.ColorControl), rect);
1241                         }
1242
1243                         dc.FillRectangle (theme.ResPool.GetSolidBrush (BackColor), items_area);
1244
1245                         if (Items.Count == 0)
1246                                 return;
1247
1248                         for (int i = top_index; i <= last_visible_index; i++) {
1249                                 Rectangle rect = GetItemDisplayRectangle (i, top_index);
1250
1251                                 if (!clip.IntersectsWith (rect))
1252                                         continue;
1253
1254                                 DrawItemState state = DrawItemState.None;
1255
1256                                 if (SelectedIndices.Contains (i))
1257                                         state |= DrawItemState.Selected;
1258                                         
1259                                 if (has_focus && FocusedItem == i)
1260                                         state |= DrawItemState.Focus;
1261                                         
1262                                 if (MultiColumn == false && hscrollbar != null && hscrollbar.Visible) {
1263                                         rect.X -= hscrollbar.Value;
1264                                         rect.Width += hscrollbar.Value;
1265                                 }
1266
1267                                 OnDrawItem (new DrawItemEventArgs (dc, Font, rect, i, state, ForeColor, BackColor));
1268                         }
1269                 }
1270
1271                 // Converts a GetItemRectangle to a one that we can display
1272                 internal Rectangle GetItemDisplayRectangle (int index, int first_displayble)
1273                 {
1274                         Rectangle item_rect;
1275                         Rectangle first_item_rect = GetItemRectangle (first_displayble);
1276                         item_rect = GetItemRectangle (index);
1277                         item_rect.X -= first_item_rect.X;
1278                         item_rect.Y -= first_item_rect.Y;
1279                         
1280                         // Subtract the checkboxes from the width
1281                         if (this is CheckedListBox)
1282                                 item_rect.Width -= 14;
1283
1284                         return item_rect;
1285                 }
1286
1287                 // Value Changed
1288                 private void HorizontalScrollEvent (object sender, EventArgs e)
1289                 {
1290                         if (multicolumn) {
1291                                 int top_item = top_index;
1292                                 int last_item = last_visible_index;
1293
1294                                 top_index = RowCount * hscrollbar.Value;
1295                                 last_visible_index = LastVisibleItem ();
1296
1297                                 if (top_item != top_index || last_item != last_visible_index)
1298                                         Invalidate (items_area);
1299                         }
1300                         else {
1301                                 int old_offset = hbar_offset;
1302                                 hbar_offset = hscrollbar.Value;
1303
1304                                 if (hbar_offset < 0)
1305                                         hbar_offset = 0;
1306
1307                                 if (IsHandleCreated)
1308                                         XplatUI.ScrollWindow (Handle, items_area, old_offset - hbar_offset, 0, false);
1309                         }
1310                 }
1311
1312                 // Only returns visible points. The diference of with IndexFromPoint is that the rectangle
1313                 // has screen coordinates
1314                 private int IndexAtClientPoint (int x, int y)
1315                 {       
1316                         if (Items.Count == 0)
1317                                 return -1;
1318                         
1319                         if (x < 0)
1320                                 x = 0;
1321                         else if (x > ClientRectangle.Right)
1322                                 x = ClientRectangle.Right;
1323
1324                         if (y < 0)
1325                                 y = 0;
1326                         else if (y > ClientRectangle.Bottom)
1327                                 y = ClientRectangle.Bottom;
1328
1329                         for (int i = top_index; i <= last_visible_index; i++)
1330                                 if (GetItemDisplayRectangle (i, top_index).Contains (x, y))
1331                                         return i;
1332
1333                         return -1;
1334                 }
1335
1336                 internal override bool IsInputCharInternal (char charCode)
1337                 {
1338                         return true;
1339                 }
1340
1341                 private int LastVisibleItem ()
1342                 {
1343                         Rectangle item_rect;
1344                         int top_y = items_area.Y + items_area.Height;
1345                         int i = 0;
1346
1347                         if (top_index >= Items.Count)
1348                                 return top_index;
1349
1350                         for (i = top_index; i < Items.Count; i++) {
1351                                 item_rect = GetItemDisplayRectangle (i, top_index);
1352                                 if (MultiColumn) {
1353                                         if (item_rect.X > items_area.Width)
1354                                                 return i - 1;
1355                                 } else {
1356                                         if (item_rect.Y + item_rect.Height > top_y)
1357                                                 return i;
1358                                 }
1359                         }
1360                         return i - 1;
1361                 }
1362
1363                 private void UpdateTopItem ()
1364                 {
1365                         if (MultiColumn) {
1366                                 int col = top_index / RowCount;
1367                                 
1368                                 if (col > hscrollbar.Maximum)
1369                                         hscrollbar.Value = hscrollbar.Maximum;
1370                                 else
1371                                         hscrollbar.Value = col;
1372                         } else {
1373                                 int val = vscrollbar.Value;
1374                                 if (top_index > vscrollbar.Maximum)
1375                                         vscrollbar.Value = vscrollbar.Maximum;
1376                                 else
1377                                         vscrollbar.Value = top_index;
1378                                 Scroll (vscrollbar, vscrollbar.Value - top_index);
1379                                 if (IsHandleCreated)
1380                                         XplatUI.ScrollWindow (Handle, items_area, 0, ItemHeight * (val - vscrollbar.Value), false);
1381                         }
1382                 }
1383                 
1384                 // Navigates to the indicated item and returns the new item
1385                 private int NavigateItemVisually (ItemNavigation navigation)
1386                 {                       
1387                         int page_size, columns, selected_index = -1;
1388
1389                         if (multicolumn) {
1390                                 columns = items_area.Width / ColumnWidthInternal; 
1391                                 page_size = columns * RowCount;
1392                                 if (page_size == 0) {
1393                                         page_size = RowCount;
1394                                 }
1395                         } else {
1396                                 page_size = items_area.Height / ItemHeight;     
1397                         }
1398
1399                         switch (navigation) {
1400
1401                         case ItemNavigation.PreviousColumn: {
1402                                 if (SelectedIndex - RowCount < 0) {
1403                                         return -1;
1404                                 }
1405
1406                                 if (SelectedIndex - RowCount < top_index) {
1407                                         top_index = SelectedIndex - RowCount;
1408                                         UpdateTopItem ();
1409                                 }
1410                                         
1411                                 selected_index = SelectedIndex - RowCount;
1412                                 break;
1413                         }
1414                         
1415                         case ItemNavigation.NextColumn: {
1416                                 if (SelectedIndex + RowCount >= Items.Count) {
1417                                         break;
1418                                 }
1419
1420                                 if (SelectedIndex + RowCount > last_visible_index) {
1421                                         top_index = SelectedIndex;
1422                                         UpdateTopItem ();
1423                                 }
1424                                         
1425                                 selected_index = SelectedIndex + RowCount;
1426                                 break;
1427                         }
1428
1429                         case ItemNavigation.First: {
1430                                 top_index = 0;
1431                                 selected_index  = 0;
1432                                 UpdateTopItem ();
1433                                 break;
1434                         }
1435
1436                         case ItemNavigation.Last: {
1437
1438                                 int rows = items_area.Height / ItemHeight;
1439                                 
1440                                 if (multicolumn) {
1441                                         selected_index = Items.Count - 1;
1442                                         break;
1443                                 }
1444                                 if (Items.Count < rows) {
1445                                         top_index = 0;
1446                                         selected_index  = Items.Count - 1;
1447                                         UpdateTopItem ();
1448                                 } else {
1449                                         top_index = Items.Count - rows;
1450                                         selected_index  = Items.Count - 1;
1451                                         UpdateTopItem ();
1452                                 }
1453                                 break;
1454                         }
1455
1456                         case ItemNavigation.Next: {
1457                                 if (FocusedItem == Items.Count - 1)
1458                                         return -1;
1459
1460                                 if (multicolumn) {
1461                                         selected_index = FocusedItem + 1;
1462                                         break;
1463                                 }
1464                                 
1465                                 int bottom = 0;
1466                                 ArrayList heights = new ArrayList ();
1467                                 if (draw_mode == DrawMode.OwnerDrawVariable) {
1468                                         for (int i = top_index; i <= FocusedItem + 1; i++) {
1469                                                 int h = GetItemHeight (i);
1470                                                 bottom += h;
1471                                                 heights.Add (h);
1472                                         }
1473                                 } else {
1474                                         bottom = ((FocusedItem + 1) - top_index + 1) * ItemHeight;
1475                                 }
1476
1477                                 if (bottom >= items_area.Height) {
1478                                         int overhang = bottom - items_area.Height;
1479
1480                                         int offset = 0;
1481                                         if (draw_mode == DrawMode.OwnerDrawVariable)
1482                                                 while (overhang > 0)
1483                                                         overhang -= (int) heights [offset];
1484                                         else
1485                                                 offset = (int) Math.Ceiling ((float)overhang / (float) ItemHeight);
1486                                         top_index += offset;
1487                                         UpdateTopItem ();
1488                                 }
1489                                 selected_index = FocusedItem + 1;
1490                                 break;
1491                         }
1492
1493                         case ItemNavigation.Previous: {
1494                                 if (FocusedItem > 0) {
1495                                         if (FocusedItem - 1 < top_index) {
1496                                                 top_index--;
1497                                                 UpdateTopItem ();
1498                                         }
1499                                         selected_index = FocusedItem - 1;
1500                                 }
1501                                 break;
1502                         }
1503
1504                         case ItemNavigation.NextPage: {
1505                                 if (Items.Count < page_size) {
1506                                         NavigateItemVisually (ItemNavigation.Last);
1507                                         break;
1508                                 }
1509
1510                                 if (FocusedItem + page_size - 1 >= Items.Count) {
1511                                         top_index = Items.Count - page_size;
1512                                         UpdateTopItem ();
1513                                         selected_index = Items.Count - 1;
1514                                 }
1515                                 else {
1516                                         if (FocusedItem + page_size - 1  > last_visible_index) {
1517                                                 top_index = FocusedItem;
1518                                                 UpdateTopItem ();
1519                                         }
1520                                         
1521                                         selected_index = FocusedItem + page_size - 1;
1522                                 }
1523                                         
1524                                 break;
1525                         }                       
1526
1527                         case ItemNavigation.PreviousPage: {
1528                                         
1529                                 int rows = items_area.Height / ItemHeight;
1530                                 if (FocusedItem - (rows - 1) <= 0) {
1531                                         top_index = 0;
1532                                         UpdateTopItem ();
1533                                         selected_index = 0;
1534                                 }
1535                                 else { 
1536                                         if (SelectedIndex - (rows - 1)  < top_index) {
1537                                                 top_index = FocusedItem - (rows - 1);
1538                                                 UpdateTopItem ();
1539                                         }
1540                                         
1541                                         selected_index = FocusedItem - (rows - 1);
1542                                 }
1543                                         
1544                                 break;
1545                         }               
1546                         default:
1547                                 break;
1548                         }
1549                         
1550                         return selected_index;
1551                 }
1552                 
1553                 
1554                 private void OnGotFocus (object sender, EventArgs e)
1555                 {
1556                         if (Items.Count == 0)
1557                                 return;
1558
1559                         if (FocusedItem == -1)
1560                                 FocusedItem = 0;
1561
1562                         InvalidateItem (FocusedItem);
1563                 }
1564                 
1565                 private void OnLostFocus (object sender, EventArgs e)
1566                 {
1567                         if (FocusedItem != -1)
1568                                 InvalidateItem (FocusedItem);
1569                 }
1570
1571                 private bool KeySearch (Keys key)
1572                 {
1573                         char c = (char) key;
1574                         if (!Char.IsLetterOrDigit (c))
1575                                 return false;
1576
1577                         int idx = FindString (c.ToString (), SelectedIndex);
1578                         if (idx != ListBox.NoMatches)
1579                                 SelectedIndex = idx;
1580
1581                         return true;
1582                 }
1583
1584                 internal void OnKeyDownLB (object sender, KeyEventArgs e)
1585                 {
1586                         int new_item = -1;
1587                         
1588                         if (Items.Count == 0)
1589                                 return;
1590
1591                         if (KeySearch (e.KeyCode))
1592                                 return;
1593
1594                         switch (e.KeyCode) {
1595                                 
1596                                 case Keys.ControlKey:
1597                                         ctrl_pressed = true;
1598                                         break;
1599                                         
1600                                 case Keys.ShiftKey:
1601                                         shift_pressed = true;
1602                                         break;
1603                                         
1604                                 case Keys.Home:
1605                                         new_item = NavigateItemVisually (ItemNavigation.First);
1606                                         break;
1607
1608                                 case Keys.End:
1609                                         new_item = NavigateItemVisually (ItemNavigation.Last);
1610                                         break;
1611
1612                                 case Keys.Up:
1613                                         new_item = NavigateItemVisually (ItemNavigation.Previous);
1614                                         break;
1615         
1616                                 case Keys.Down:
1617                                         new_item = NavigateItemVisually (ItemNavigation.Next);
1618                                         break;
1619                                 
1620                                 case Keys.PageUp:
1621                                         new_item = NavigateItemVisually (ItemNavigation.PreviousPage);
1622                                         break;
1623         
1624                                 case Keys.PageDown:
1625                                         new_item = NavigateItemVisually (ItemNavigation.NextPage);
1626                                         break;
1627
1628                                 case Keys.Right:
1629                                         if (multicolumn == true) {
1630                                                 new_item = NavigateItemVisually (ItemNavigation.NextColumn);
1631                                         }
1632                                         break;
1633         
1634                                 case Keys.Left:
1635                                         if (multicolumn == true) {
1636                                                 new_item = NavigateItemVisually (ItemNavigation.PreviousColumn);
1637                                         }
1638                                         break;
1639                                         
1640                                 case Keys.Space:
1641                                         if (selection_mode == SelectionMode.MultiSimple) {
1642                                                 SelectedItemFromNavigation (FocusedItem);
1643                                         }
1644                                         break;
1645                                 
1646
1647                                 default:
1648                                         break;
1649                                 }
1650                                 
1651                                 if (new_item != -1) {
1652                                         FocusedItem = new_item;
1653                                         if (selection_mode != SelectionMode.MultiSimple && selection_mode != SelectionMode.None) {
1654                                                 SelectedItemFromNavigation (new_item);
1655                                         }
1656                                 }
1657                 }
1658                 
1659                 private void OnKeyUpLB (object sender, KeyEventArgs e)
1660                 {
1661                         switch (e.KeyCode) {
1662                                 case Keys.ControlKey:
1663                                         ctrl_pressed = false;
1664                                         break;
1665                                 case Keys.ShiftKey:
1666                                         shift_pressed = false;
1667                                         break;
1668                                 default: 
1669                                         break;
1670                         }
1671                 }
1672
1673                 internal void InvalidateItem (int index)
1674                 {
1675                         if (!IsHandleCreated)
1676                                 return;
1677                         Rectangle bounds = GetItemDisplayRectangle (index, top_index);
1678                         if (ClientRectangle.IntersectsWith (bounds))
1679                                 Invalidate (bounds);
1680                 }
1681
1682                 internal virtual void OnItemClick (int index)
1683                 {
1684                         OnSelectedIndexChanged  (EventArgs.Empty);
1685                         OnSelectedValueChanged (EventArgs.Empty);
1686                 }
1687
1688                 int anchor = -1;
1689                 int[] prev_selection;
1690                 bool button_pressed = false;
1691
1692                 private void SelectExtended (int index)
1693                 {
1694                         SuspendLayout ();
1695
1696                         ArrayList new_selection = new ArrayList ();
1697                         int start = anchor < index ? anchor : index;
1698                         int end = anchor > index ? anchor : index;
1699                         for (int i = start; i <= end; i++)
1700                                 new_selection.Add (i);
1701
1702                         if (ctrl_pressed)
1703                                 foreach (int i in prev_selection)
1704                                         if (!selected_indices.Contains (i))
1705                                                 new_selection.Add (i);
1706
1707                         // Need to make a copy since we can't enumerate and modify the collection
1708                         // at the same time
1709                         ArrayList sel_indices = (ArrayList) selected_indices.List.Clone ();
1710                         foreach (int i in sel_indices)
1711                                 if (!new_selection.Contains (i))
1712                                         selected_indices.Remove (i);
1713
1714                         foreach (int i in new_selection)
1715                                 if (!sel_indices.Contains (i))
1716                                         selected_indices.Add (i);
1717                         ResumeLayout ();
1718                 }
1719
1720                 private void OnMouseDownLB (object sender, MouseEventArgs e)
1721                 {
1722                         // Only do stuff with the left mouse button
1723                         if ((e.Button & MouseButtons.Left) == 0)
1724                                 return;
1725                         
1726                         int index = IndexAtClientPoint (e.X, e.Y);
1727                         if (index == -1)
1728                                 return;
1729
1730                         switch (SelectionMode) {
1731                         case SelectionMode.One:
1732                                 SelectedIndex = index;
1733                                 break;
1734
1735                         case SelectionMode.MultiSimple:
1736                                 if (SelectedIndices.Contains (index))
1737                                         SelectedIndices.Remove (index);
1738                                 else
1739                                         SelectedIndices.Add (index);
1740                                 break;
1741
1742                         case SelectionMode.MultiExtended:
1743                                 shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1744                                 ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1745
1746                                 if (ctrl_pressed) {
1747                                         prev_selection = new int [SelectedIndices.Count];
1748                                         SelectedIndices.CopyTo (prev_selection, 0);
1749                                 } else
1750                                         SelectedIndices.Clear ();
1751
1752                                 if (!shift_pressed)
1753                                         anchor = index;
1754
1755                                 SelectExtended (index);
1756                                 break;
1757
1758                         case SelectionMode.None:
1759                                 break;
1760                         default:
1761                                 return;
1762                         }
1763
1764                         button_pressed = true;
1765                         FocusedItem = index;
1766                 }
1767
1768                 private void OnMouseMoveLB (object sender, MouseEventArgs e)
1769                 {
1770                         if (!button_pressed)
1771                                 return;
1772
1773                         int index = IndexAtClientPoint (e.X, e.Y);
1774
1775                         switch (SelectionMode) {
1776                         case SelectionMode.One:
1777                                 SelectedIndex = index;
1778                                 break;
1779
1780                         case SelectionMode.MultiSimple:
1781                                 break;
1782
1783                         case SelectionMode.MultiExtended:
1784                                 SelectExtended (index);
1785                                 break;
1786
1787                         case SelectionMode.None:
1788                                 break;
1789                         default:
1790                                 return;
1791                         }
1792
1793                         FocusedItem = index;
1794                 }
1795
1796                 private void OnMouseUpLB (object sender, MouseEventArgs e)
1797                 {
1798                         // Only do stuff with the left mouse button
1799                         if ((e.Button & MouseButtons.Left) == 0)
1800                                 return;
1801
1802                         if (e.Clicks > 1) {
1803                                 OnDoubleClick (EventArgs.Empty);
1804 #if NET_2_0
1805                                 OnMouseDoubleClick (e);
1806 #endif
1807                         }
1808                         else if (e.Clicks == 1) {
1809                                 OnClick (EventArgs.Empty);
1810 #if NET_2_0
1811                                 OnMouseClick (e);
1812 #endif
1813                         }
1814                         
1815                         if (!button_pressed)
1816                                 return;
1817
1818                         int index = IndexAtClientPoint (e.X, e.Y);
1819                         OnItemClick (index);
1820                         button_pressed = ctrl_pressed = shift_pressed = false;
1821                 }
1822
1823                 private void Scroll (ScrollBar scrollbar, int delta)
1824                 {
1825                         if (delta == 0 || !scrollbar.Visible || !scrollbar.Enabled)
1826                                 return;
1827
1828                         int max;
1829                         if (scrollbar == hscrollbar)
1830                                 max = hscrollbar.Maximum - (items_area.Width / ColumnWidthInternal) + 1;
1831                         else
1832                                 max = vscrollbar.Maximum - (items_area.Height / ItemHeight) + 1;
1833
1834                         int val = scrollbar.Value + delta;
1835                         if (val > max)
1836                                 val = max;
1837                         else if (val < scrollbar.Minimum)
1838                                 val = scrollbar.Minimum;
1839                         scrollbar.Value = val;
1840                 }
1841
1842                 private void OnMouseWheelLB (object sender, MouseEventArgs me)
1843                 {
1844                         if (Items.Count == 0)
1845                                 return;
1846
1847                         int lines = me.Delta / 120;
1848
1849                         if (MultiColumn)
1850                                 Scroll (hscrollbar, -SystemInformation.MouseWheelScrollLines * lines);
1851                         else
1852                                 Scroll (vscrollbar, -lines);
1853                 }
1854
1855                 internal override void OnPaintInternal (PaintEventArgs pevent)
1856                 {
1857                         if (suspend_layout)
1858                                 return;
1859
1860                         Draw (pevent.ClipRectangle, pevent.Graphics);
1861                 }
1862
1863                 internal void RepositionScrollBars ()
1864                 {
1865                         if (vscrollbar.is_visible) {
1866                                 vscrollbar.Size = new Size (vscrollbar.Width, items_area.Height);
1867                                 vscrollbar.Location = new Point (items_area.Width, 0);
1868                         }
1869
1870                         if (hscrollbar.is_visible) {
1871                                 hscrollbar.Size = new Size (items_area.Width, hscrollbar.Height);
1872                                 hscrollbar.Location = new Point (0, items_area.Height);
1873                         }
1874                 }
1875
1876                 // An item navigation operation (mouse or keyboard) has caused to select a new item
1877                 internal void SelectedItemFromNavigation (int index)
1878                 {
1879                         switch (SelectionMode) {
1880                                 case SelectionMode.None: // Do nothing
1881                                         break;
1882                                 case SelectionMode.One: {
1883                                         SelectedIndex = index;
1884                                         break;
1885                                 }
1886                                 case SelectionMode.MultiSimple: {
1887                                         if (SelectedIndex == -1) {
1888                                                 SelectedIndex = index;
1889                                         } else {
1890
1891                                                 if (SelectedIndices.Contains (index))
1892                                                         SelectedIndices.Remove (index);
1893                                                 else {
1894                                                         SelectedIndices.AddCore (index);
1895
1896                                                         OnSelectedIndexChanged  (EventArgs.Empty);
1897                                                         OnSelectedValueChanged (EventArgs.Empty);
1898                                                 }
1899                                         }
1900                                         break;
1901                                 }
1902                                 
1903                                 case SelectionMode.MultiExtended: {
1904                                         if (SelectedIndex == -1) {
1905                                                 SelectedIndex = index;
1906                                         } else {
1907
1908                                                 if (ctrl_pressed == false && shift_pressed == false) {
1909                                                         SelectedIndices.Clear ();
1910                                                 }
1911                                                 
1912                                                 if (shift_pressed == true) {
1913                                                         ShiftSelection (index);
1914                                                 } else { // ctrl_pressed or single item
1915                                                         SelectedIndices.AddCore (index);
1916
1917                                                 }
1918
1919                                                 OnSelectedIndexChanged  (EventArgs.Empty);
1920                                                 OnSelectedValueChanged (EventArgs.Empty);
1921                                         }
1922                                         break;
1923                                 }
1924                                 
1925                                 default:
1926                                         break;
1927                         }
1928                 }
1929                 
1930                 private void ShiftSelection (int index)
1931                 {
1932                         int shorter_item = -1, dist = Items.Count + 1, cur_dist;
1933                         
1934                         foreach (int idx in selected_indices) {
1935                                 if (idx > index) {
1936                                         cur_dist = idx - index;
1937                                 } else {
1938                                         cur_dist = index - idx;
1939                                 }
1940
1941                                 if (cur_dist < dist) {
1942                                         dist = cur_dist;
1943                                         shorter_item = idx;
1944                                 }
1945                         }
1946                         
1947                         if (shorter_item != -1) {
1948                                 int start, end;
1949                                 
1950                                 if (shorter_item > index) {
1951                                         start = index;
1952                                         end = shorter_item;
1953                                 } else {
1954                                         start = shorter_item;
1955                                         end = index;
1956                                 }
1957                                 
1958                                 selected_indices.Clear ();
1959                                 for (int idx = start; idx <= end; idx++) {
1960                                         selected_indices.AddCore (idx);
1961                                 }
1962                         }
1963                 }
1964                 
1965                 internal int FocusedItem {
1966                         get { return focused_item; }
1967                         set {
1968                                 if (focused_item == value)
1969                                         return;
1970
1971                                 int prev = focused_item;
1972                         
1973                                 focused_item = value;
1974                         
1975                                 if (has_focus == false)
1976                                         return;
1977
1978                                 if (prev != -1)
1979                                         InvalidateItem (prev);
1980                         
1981                                 if (value != -1)
1982                                         InvalidateItem (value);
1983                         }
1984                 }
1985
1986                 StringFormat string_format;
1987                 internal StringFormat StringFormat {
1988                         get {
1989                                 if (string_format == null) {
1990                                         string_format = new StringFormat ();
1991                                         if (RightToLeft == RightToLeft.Yes)
1992                                                 string_format.Alignment = StringAlignment.Far;
1993                                         else
1994                                                 string_format.Alignment = StringAlignment.Near;
1995                                         CalculateTabStops ();
1996                                 }
1997                                 return string_format;
1998                         }
1999                 }
2000
2001                 internal virtual void CollectionChanged ()
2002                 {
2003                         if (sorted) 
2004                                 Sort (false);
2005
2006                         if (Items.Count == 0) {
2007                                 selected_indices.List.Clear ();
2008                                 focused_item = -1;
2009                                 top_index = 0;
2010                         }
2011                         if (Items.Count <= focused_item)
2012                                 focused_item = Items.Count - 1;
2013
2014                         if (!IsHandleCreated || suspend_layout)
2015                                 return;
2016
2017                         LayoutListBox ();
2018
2019                         base.Refresh ();
2020                 }
2021
2022                 void EnsureVisible (int index)
2023                 {
2024                         if (!IsHandleCreated || index == -1)
2025                                 return;
2026
2027                         if (index < top_index) {
2028                                 top_index = index;
2029                                 UpdateTopItem ();
2030                                 Invalidate ();
2031                         } else if (!multicolumn) {
2032                                 int rows = items_area.Height / ItemHeight;
2033                                 if (index >= (top_index + rows))
2034                                         top_index = index - rows + 1;
2035
2036                                 UpdateTopItem ();
2037                         } else {
2038                                 int rows = Math.Max (1, items_area.Height / ItemHeight);
2039                                 int cols = Math.Max (1, items_area.Width / ColumnWidthInternal);
2040                                 
2041                                 if (index >= (top_index + (rows * cols))) {
2042                                         int incolumn = index / rows;
2043                                         top_index = (incolumn - (cols - 1)) * rows;
2044
2045                                         UpdateTopItem ();
2046                                         Invalidate ();
2047                                 }
2048                         }
2049                 }
2050
2051                 private void UpdateListBoxBounds ()
2052                 {
2053                         if (IsHandleCreated)
2054                                 SetBoundsInternal (bounds.X, bounds.Y, bounds.Width, IntegralHeight ? SnapHeightToIntegral (requested_height) : requested_height, BoundsSpecified.None);
2055                 }
2056
2057                 private void UpdateScrollBars ()
2058                 {
2059                         items_area = ClientRectangle;
2060                         if (UpdateHorizontalScrollBar ()) {
2061                                 items_area.Height -= hscrollbar.Height;
2062                                 if (UpdateVerticalScrollBar ()) {
2063                                         items_area.Width -= vscrollbar.Width;
2064                                         UpdateHorizontalScrollBar ();
2065                                 }
2066                         } else if (UpdateVerticalScrollBar ()) {
2067                                 items_area.Width -= vscrollbar.Width;
2068                                 if (UpdateHorizontalScrollBar ()) {
2069                                         items_area.Height -= hscrollbar.Height;
2070                                         UpdateVerticalScrollBar ();
2071                                 }
2072                         }
2073
2074                         RepositionScrollBars ();
2075                 }
2076
2077                 /* Determines if the horizontal scrollbar has to be displyed */
2078                 private bool UpdateHorizontalScrollBar ()
2079                 {
2080                         bool show = false;
2081                         bool enabled = true;
2082
2083                         if (MultiColumn) {
2084                                 if (canvas_size.Width > items_area.Width) {
2085                                         show = true;
2086                                         hscrollbar.Maximum  = canvas_size.Width / ColumnWidthInternal - 1;
2087                                 } else if (ScrollAlwaysVisible == true) {
2088                                         enabled = false;
2089                                         show = true;
2090                                         hscrollbar.Maximum  = 0;
2091                                 }
2092                         } else if (canvas_size.Width > ClientRectangle.Width && HorizontalScrollbar) {
2093                                 show = true;
2094                                 hscrollbar.Maximum = canvas_size.Width;
2095                                 hscrollbar.LargeChange = Math.Max (0, items_area.Width);
2096                         } else if (horizontal_scrollbar) {
2097                                 show = true;
2098                                 enabled = false;
2099                                 hscrollbar.Maximum = 0;
2100                         }
2101
2102                         hbar_offset = hscrollbar.Value;
2103                         hscrollbar.Enabled = enabled;
2104                         hscrollbar.Visible = show;
2105
2106                         return show;
2107                 }
2108
2109                 /* Determines if the vertical scrollbar has to be displyed */
2110                 private bool UpdateVerticalScrollBar ()
2111                 {
2112                         if (MultiColumn || (Items.Count == 0 && !scroll_always_visible)) {
2113                                 vscrollbar.Visible = false;
2114                                 return false;
2115                         } else if (Items.Count == 0) {
2116                                 vscrollbar.Visible = true;
2117                                 vscrollbar.Enabled = false;
2118                                 vscrollbar.Maximum = 0;
2119                                 return true;
2120                         }
2121
2122                         bool show = false;
2123                         bool enabled = true;
2124                         if (canvas_size.Height > items_area.Height) {
2125                                 show = true;
2126                                 vscrollbar.Maximum = Items.Count - 1;
2127                                 vscrollbar.LargeChange = Math.Max (items_area.Height / ItemHeight, 0);
2128                         } else if (ScrollAlwaysVisible) {
2129                                 show = true;
2130                                 enabled = false;
2131                                 vscrollbar.Maximum = 0;
2132                         }
2133
2134                         vscrollbar.Enabled = enabled;
2135                         vscrollbar.Visible = show;
2136
2137                         return show;
2138                 }
2139
2140                 // Value Changed
2141                 private void VerticalScrollEvent (object sender, EventArgs e)
2142                 {
2143                         int top_item = top_index;
2144
2145                         top_index = /*row_count + */ vscrollbar.Value;
2146                         last_visible_index = LastVisibleItem ();
2147
2148                         int delta = (top_item - top_index) * ItemHeight;
2149                         if (DrawMode == DrawMode.OwnerDrawVariable) {
2150                                 delta = 0;
2151
2152                                 if (top_index < top_item)
2153                                         for (int i = top_index; i < top_item; i++)
2154                                                 delta += GetItemHeight (i);
2155                                 else 
2156                                         for (int i = top_item; i < top_index; i++)
2157                                                 delta -= GetItemHeight (i);
2158                         }
2159
2160                         if (IsHandleCreated)
2161                                 XplatUI.ScrollWindow (Handle, items_area, 0, delta, false);
2162                 }
2163
2164                 #endregion Private Methods
2165
2166 #if NET_2_0
2167                 public class IntegerCollection : IList, ICollection, IEnumerable
2168                 {
2169                         private ListBox owner;
2170                         private List<int> list;
2171                         
2172                         #region Public Constructor
2173                         public IntegerCollection (ListBox owner)
2174                         {
2175                                 this.owner = owner;
2176                                 list = new List<int> ();
2177                         }
2178                         #endregion
2179
2180                         #region Public Properties
2181                         [Browsable (false)]
2182                         public int Count {
2183                                 get { return list.Count; }
2184                         }
2185                         
2186                         public int this [int index] {
2187                                 get { return list[index]; }
2188                                 set { list[index] = value; owner.CalculateTabStops (); }
2189                         }
2190                         #endregion
2191
2192                         #region Public Methods
2193                         public int Add (int item)
2194                         {
2195                                 // This collection does not allow duplicates
2196                                 if (!list.Contains (item)) {
2197                                         list.Add (item);
2198                                         list.Sort ();
2199                                         owner.CalculateTabStops ();
2200                                 }
2201                                 
2202                                 return list.IndexOf (item);
2203                         }
2204                         
2205                         public void AddRange (int[] items)
2206                         {
2207                                 foreach (int i in items)
2208                                         if (!list.Contains (i))
2209                                                 list.Add (i);
2210                                                 
2211                                 list.Sort ();
2212                         }
2213                         
2214                         public void AddRange (IntegerCollection value)
2215                         {
2216                                 foreach (int i in value)
2217                                         if (!list.Contains (i))
2218                                                 list.Add (i);
2219
2220                                 list.Sort ();
2221                         }
2222                         
2223                         public void Clear ()
2224                         {
2225                                 list.Clear ();
2226                                 owner.CalculateTabStops ();
2227                         }
2228                         
2229                         public bool Contains (int item)
2230                         {
2231                                 return list.Contains (item);
2232                         }
2233                         
2234                         public void CopyTo (Array destination, int index)
2235                         {
2236                                 for (int i = index; i < list.Count; i++)
2237                                         destination.SetValue (list[i], i);
2238                         }
2239                         
2240                         public int IndexOf (int item)
2241                         {
2242                                 return list.IndexOf (item);
2243                         }
2244                         
2245                         public void Remove (int item)
2246                         {
2247                                 list.Remove (item);
2248                                 list.Sort ();
2249                                 owner.CalculateTabStops ();
2250                         }
2251                         
2252                         public void RemoveAt (int index)
2253                         {
2254                                 list.RemoveAt (index);
2255                                 list.Sort ();
2256                                 owner.CalculateTabStops ();
2257                         }
2258                         #endregion
2259
2260                         #region IEnumerable Members
2261                         IEnumerator IEnumerable.GetEnumerator ()
2262                         {
2263                                 return list.GetEnumerator ();
2264                         }
2265                         #endregion
2266
2267                         #region IList Members
2268                         int IList.Add (object item)
2269                         {
2270                                 return Add ((int) item);
2271                         }
2272
2273                         void IList.Clear ()
2274                         {
2275                                 Clear ();
2276                         }
2277
2278                         bool IList.Contains (object item)
2279                         {
2280                                 return Contains ((int) item);
2281                         }
2282
2283                         int IList.IndexOf (object item)
2284                         {
2285                                 return IndexOf ((int) item);
2286                         }
2287
2288                         void IList.Insert (int index, object value)
2289                         {
2290                                 throw new Exception ("The method or operation is not implemented.");
2291                         }
2292
2293                         bool IList.IsFixedSize
2294                         {
2295                                 get { return false; }
2296                         }
2297
2298                         bool IList.IsReadOnly
2299                         {
2300                                 get { return false; }
2301                         }
2302
2303                         void IList.Remove (object value)
2304                         {
2305                                 Remove ((int)value);
2306                         }
2307
2308                         void IList.RemoveAt (int index)
2309                         {
2310                                 RemoveAt (index);
2311                         }
2312
2313                         object IList.this[int index] {
2314                                 get { return this[index]; }
2315                                 set { this[index] = (int)value; }
2316                         }
2317                         #endregion
2318
2319                         #region ICollection Members
2320                         bool ICollection.IsSynchronized
2321                         {
2322                                 get { throw new Exception ("The method or operation is not implemented."); }
2323                         }
2324
2325                         object ICollection.SyncRoot
2326                         {
2327                                 get { throw new Exception ("The method or operation is not implemented."); }
2328                         }
2329                         #endregion
2330                 }
2331 #endif
2332
2333                 [ListBindable (false)]
2334                 public class ObjectCollection : IList, ICollection, IEnumerable
2335                 {
2336                         internal class ListObjectComparer : IComparer
2337                         {
2338                                 public int Compare (object a, object b)
2339                                 {
2340                                         string str1 = a.ToString ();
2341                                         string str2 = b.ToString ();
2342                                         return str1.CompareTo (str2);
2343                                 }
2344                         }
2345
2346                         private ListBox owner;
2347                         internal ArrayList object_items = new ArrayList ();
2348
2349                         public ObjectCollection (ListBox owner)
2350                         {
2351                                 this.owner = owner;
2352                         }
2353
2354                         public ObjectCollection (ListBox owner, object[] value)
2355                         {
2356                                 this.owner = owner;
2357                                 AddRange (value);
2358                         }
2359
2360                         public ObjectCollection (ListBox owner,  ObjectCollection value)
2361                         {
2362                                 this.owner = owner;
2363                                 AddRange (value);
2364                         }
2365
2366                         #region Public Properties
2367                         public int Count {
2368                                 get { return object_items.Count; }
2369                         }
2370
2371                         public bool IsReadOnly {
2372                                 get { return false; }
2373                         }
2374
2375                         [Browsable(false)]
2376                         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2377                         public virtual object this [int index] {
2378                                 get {
2379                                         if (index < 0 || index >= Count)
2380                                                 throw new ArgumentOutOfRangeException ("Index of out range");
2381
2382                                         return object_items[index];
2383                                 }
2384                                 set {
2385                                         if (index < 0 || index >= Count)
2386                                                 throw new ArgumentOutOfRangeException ("Index of out range");
2387                                         if (value == null)
2388                                                 throw new ArgumentNullException ("value");
2389
2390                                         object_items[index] = value;
2391                                         owner.CollectionChanged ();
2392                                 }
2393                         }
2394
2395                         bool ICollection.IsSynchronized {
2396                                 get { return false; }
2397                         }
2398
2399                         object ICollection.SyncRoot {
2400                                 get { return this; }
2401                         }
2402
2403                         bool IList.IsFixedSize {
2404                                 get { return false; }
2405                         }
2406
2407                         #endregion Public Properties
2408                         
2409                         #region Public Methods
2410                         public int Add (object item)
2411                         {
2412                                 int idx;
2413
2414                                 idx = AddItem (item);
2415                                 owner.CollectionChanged ();
2416                                 return idx;
2417                         }
2418
2419                         public void AddRange (object[] items)
2420                         {
2421                                 if (items == null)
2422                                         throw new ArgumentNullException ("items");
2423
2424                                 foreach (object mi in items)
2425                                         AddItem (mi);
2426
2427                                 owner.CollectionChanged ();
2428                         }
2429
2430                         public void AddRange (ObjectCollection value)
2431                         {
2432                                 if (value == null)
2433                                         throw new ArgumentNullException ("value");
2434
2435                                 foreach (object mi in value)
2436                                         AddItem (mi);
2437
2438                                 owner.CollectionChanged ();
2439                         }
2440
2441                         internal void AddRange (IList list)
2442                         {
2443                                 foreach (object mi in list)
2444                                         AddItem (mi);
2445
2446                                 owner.CollectionChanged ();
2447                         }
2448
2449                         public virtual void Clear ()
2450                         {
2451                                 owner.selected_indices.Clear ();
2452                                 object_items.Clear ();
2453                                 owner.CollectionChanged ();
2454                         }
2455                         public bool Contains (object value)
2456                         {
2457                                 if (value == null)
2458                                         throw new ArgumentNullException ("value");
2459
2460                                 return object_items.Contains (value);
2461                         }
2462
2463 #if NET_2_0
2464                         public void CopyTo (object[] destination, int arrayIndex)
2465                         {
2466                                 object [] dest = destination;
2467 #else
2468                         public void CopyTo (object [] dest, int arrayIndex)
2469                         {
2470 #endif
2471                                 object_items.CopyTo (dest, arrayIndex);
2472                         }
2473
2474 #if NET_2_0
2475                         void ICollection.CopyTo (Array destination, int index)
2476                         {
2477                                 Array dest = destination;
2478 #else
2479                         void ICollection.CopyTo (Array dest, int index)
2480                         {
2481 #endif
2482                                 object_items.CopyTo (dest, index);
2483                         }
2484
2485                         public IEnumerator GetEnumerator ()
2486                         {
2487                                 return object_items.GetEnumerator ();
2488                         }
2489
2490                         int IList.Add (object item)
2491                         {
2492                                 return Add (item);
2493                         }
2494
2495                         public int IndexOf (object value)
2496                         {
2497                                 if (value == null)
2498                                         throw new ArgumentNullException ("value");
2499
2500                                 return object_items.IndexOf (value);
2501                         }
2502
2503                         public void Insert (int index,  object item)
2504                         {
2505                                 if (index < 0 || index > Count)
2506                                         throw new ArgumentOutOfRangeException ("Index of out range");
2507                                 if (item == null)
2508                                         throw new ArgumentNullException ("item");
2509                                         
2510                                 owner.BeginUpdate ();
2511                                 object_items.Insert (index, item);
2512                                 owner.CollectionChanged ();
2513                                 owner.EndUpdate ();
2514                         }
2515
2516                         public void Remove (object value)
2517                         {
2518                                 if (value == null)
2519                                         return;
2520
2521                                 RemoveAt (IndexOf (value));
2522                         }
2523
2524                         public void RemoveAt (int index)
2525                         {
2526                                 if (index < 0 || index >= Count)
2527                                         throw new ArgumentOutOfRangeException ("Index of out range");
2528
2529                                 owner.selected_indices.Remove (index);
2530                                 object_items.RemoveAt (index);
2531                                 owner.CollectionChanged ();
2532                         }
2533                         #endregion Public Methods
2534
2535                         #region Private Methods
2536                         internal int AddItem (object item)
2537                         {
2538                                 if (item == null)
2539                                         throw new ArgumentNullException ("item");
2540
2541                                 int cnt = object_items.Count;
2542                                 object_items.Add (item);
2543                                 return cnt;
2544                         }
2545
2546                         internal void Sort ()
2547                         {
2548                                 object_items.Sort (new ListObjectComparer ());
2549                         }
2550
2551                         #endregion Private Methods
2552                 }
2553
2554                 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2555                 {
2556                         private ListBox owner;
2557                         ArrayList selection;
2558                         bool sorting_needed; // Selection state retrieval is done sorted - we do it lazyly
2559
2560                         public SelectedIndexCollection (ListBox owner)
2561                         {
2562                                 this.owner = owner;
2563                                 selection = new ArrayList ();
2564                         }
2565
2566                         #region Public Properties
2567                         [Browsable (false)]
2568                         public int Count {
2569                                 get { return selection.Count; }
2570                         }
2571
2572                         public bool IsReadOnly {
2573                                 get { return true; }
2574                         }
2575
2576                         public int this [int index] {
2577                                 get {
2578                                         if (index < 0 || index >= Count)
2579                                                 throw new ArgumentOutOfRangeException ("Index of out range");
2580
2581                                         CheckSorted ();
2582                                         return (int)selection [index];
2583                                 }
2584                         }
2585
2586                         bool ICollection.IsSynchronized {
2587                                 get { return true; }
2588                         }
2589
2590                         bool IList.IsFixedSize{
2591                                 get { return true; }
2592                         }
2593
2594                         object ICollection.SyncRoot {
2595                                 get { return selection; }
2596                         }
2597
2598                         #endregion Public Properties
2599
2600                         #region Public Methods
2601 #if NET_2_0
2602                         public 
2603 #else
2604                         internal
2605 #endif
2606                         void Add (int index)
2607                         {
2608                                 if (AddCore (index)) {
2609                                         owner.OnSelectedIndexChanged (EventArgs.Empty);
2610                                         owner.OnSelectedValueChanged (EventArgs.Empty);
2611                                 }
2612                         }
2613
2614                         // Need to separate selection logic from events,
2615                         // since selection changes using keys/mouse handle them their own way
2616                         internal bool AddCore (int index)
2617                         {
2618                                 if (selection.Contains (index))
2619                                         return false;
2620
2621                                 if (index == -1) // Weird MS behaviour
2622                                         return false;
2623                                 if (index < -1 || index >= owner.Items.Count)
2624                                         throw new ArgumentOutOfRangeException ("index");
2625                                 if (owner.selection_mode == SelectionMode.None)
2626                                         throw new InvalidOperationException ("Cannot call this method when selection mode is SelectionMode.None");
2627
2628                                 if (owner.selection_mode == SelectionMode.One && Count > 0) // Unselect previously selected item
2629                                         RemoveCore ((int)selection [0]);
2630
2631                                 selection.Add (index);
2632                                 sorting_needed = true;
2633                                 owner.EnsureVisible (index);
2634                                 owner.FocusedItem = index;
2635                                 owner.InvalidateItem (index);
2636
2637                                 return true;
2638                         }
2639
2640 #if NET_2_0
2641                         public 
2642 #else
2643                         internal
2644 #endif
2645                         void Clear ()
2646                         {
2647                                 if (selection.Count == 0)
2648                                         return;
2649
2650                                 foreach (int index in selection)
2651                                         owner.InvalidateItem (index);
2652
2653                                 selection.Clear ();
2654                                 owner.OnSelectedIndexChanged (EventArgs.Empty);
2655                                 owner.OnSelectedValueChanged (EventArgs.Empty);
2656                         }
2657
2658                         public bool Contains (int selectedIndex)
2659                         {
2660                                 foreach (int index in selection)
2661                                         if (index == selectedIndex)
2662                                                 return true;
2663                                 return false;
2664                         }
2665
2666 #if NET_2_0
2667                         public void CopyTo (Array destination, int index)
2668                         {
2669                                 Array dest = destination;
2670 #else
2671                         public void CopyTo (Array dest, int index)
2672                         {
2673 #endif
2674                                 CheckSorted ();
2675                                 selection.CopyTo (dest, index);
2676                         }
2677
2678                         public IEnumerator GetEnumerator ()
2679                         {
2680                                 CheckSorted ();
2681                                 return selection.GetEnumerator ();
2682                         }
2683
2684                         // FIXME: Probably we can avoid sorting when calling
2685                         // IndexOf (imagine a scenario where multiple removal of items
2686                         // happens)
2687 #if NET_2_0
2688                         public 
2689 #else
2690                         internal
2691 #endif
2692                         void Remove (int index)
2693                         {
2694                                 // Separate logic from events here too
2695                                 if (RemoveCore (index)) {
2696                                         owner.OnSelectedIndexChanged (EventArgs.Empty);
2697                                         owner.OnSelectedValueChanged (EventArgs.Empty);
2698                                 }
2699                         }
2700
2701                         internal bool RemoveCore (int index)
2702                         {
2703                                 int idx = IndexOf (index);
2704                                 if (idx == -1)
2705                                         return false;
2706
2707                                 selection.RemoveAt (idx);
2708                                 owner.InvalidateItem (index);
2709
2710                                 return true;
2711                         }
2712
2713                         int IList.Add (object value)
2714                         {
2715                                 throw new NotSupportedException ();
2716                         }
2717
2718                         void IList.Clear ()
2719                         {
2720                                 throw new NotSupportedException ();
2721                         }
2722
2723                         bool IList.Contains (object selectedIndex)
2724                         {
2725                                 return Contains ((int)selectedIndex);
2726                         }
2727
2728                         int IList.IndexOf (object selectedIndex)
2729                         {
2730                                 return IndexOf ((int) selectedIndex);
2731                         }
2732
2733                         void IList.Insert (int index, object value)
2734                         {
2735                                 throw new NotSupportedException ();
2736                         }
2737
2738                         void IList.Remove (object value)
2739                         {
2740                                 throw new NotSupportedException ();
2741                         }
2742
2743                         void IList.RemoveAt (int index)
2744                         {
2745                                 throw new NotSupportedException ();
2746                         }
2747
2748                         object IList.this[int index]{
2749                                 get { return this [index]; }
2750                                 set {throw new NotImplementedException (); }
2751                         }
2752
2753                         public int IndexOf (int selectedIndex)
2754                         {
2755                                 CheckSorted ();
2756
2757                                 for (int i = 0; i < selection.Count; i++)
2758                                         if ((int)selection [i] == selectedIndex)
2759                                                 return i;
2760
2761                                 return -1;
2762                         }
2763                         #endregion Public Methods
2764                         internal ArrayList List {
2765                                 get {
2766                                         CheckSorted ();
2767                                         return selection;
2768                                 }
2769                         }
2770
2771                         void CheckSorted ()
2772                         {
2773                                 if (sorting_needed) {
2774                                         sorting_needed = false;
2775                                         selection.Sort ();
2776                                 }
2777                         }
2778                 }
2779
2780                 public class SelectedObjectCollection : IList, ICollection, IEnumerable
2781                 {
2782                         private ListBox owner;
2783
2784                         public SelectedObjectCollection (ListBox owner)
2785                         {
2786                                 this.owner = owner;
2787                         }
2788
2789                         #region Public Properties
2790                         public int Count {
2791                                 get { return owner.selected_indices.Count; }
2792                         }
2793
2794                         public bool IsReadOnly {
2795                                 get { return true; }
2796                         }
2797
2798                         [Browsable(false)]
2799                         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2800                         public object this [int index] {
2801                                 get {
2802                                         if (index < 0 || index >= Count)
2803                                                 throw new ArgumentOutOfRangeException ("Index of out range");
2804
2805                                         return owner.items [owner.selected_indices [index]];
2806                                 }
2807                                 set {throw new NotSupportedException ();}
2808                         }
2809
2810                         bool ICollection.IsSynchronized {
2811                                 get { return true; }
2812                         }
2813
2814                         object ICollection.SyncRoot {
2815                                 get { return this; }
2816                         }
2817
2818                         bool IList.IsFixedSize {
2819                                 get { return true; }
2820                         }
2821
2822                         #endregion Public Properties
2823
2824                         #region Public Methods
2825 #if NET_2_0
2826                         public void Add (object value)
2827                         {
2828                                 if (owner.selection_mode == SelectionMode.None)
2829                                         throw new ArgumentException ("Cannot call this method if SelectionMode is SelectionMode.None");
2830
2831                                 int idx = owner.items.IndexOf (value);
2832                                 if (idx == -1)
2833                                         return;
2834
2835                                 owner.selected_indices.Add (idx);
2836                         }
2837
2838                         public void Clear ()
2839                         {
2840                                 owner.selected_indices.Clear ();
2841                         }
2842 #endif
2843
2844                         public bool Contains (object selectedObject)
2845                         {
2846                                 int idx = owner.items.IndexOf (selectedObject);
2847                                 return idx == -1 ? false : owner.selected_indices.Contains (idx);
2848                         }
2849
2850 #if NET_2_0
2851                         public void CopyTo (Array destination, int index)
2852                         {
2853                                 Array dest = destination;
2854 #else
2855                         public void CopyTo (Array dest, int index)
2856                         {
2857 #endif
2858                                 for (int i = 0; i < Count; i++)
2859                                         dest.SetValue (this [i], index++);
2860                         }
2861
2862 #if NET_2_0
2863                         public void Remove (object value)
2864                         {
2865                                 if (value == null)
2866                                         return;
2867
2868                                 int idx = owner.items.IndexOf (value);
2869                                 if (idx == -1)
2870                                         return;
2871
2872                                 owner.selected_indices.Remove (idx);
2873                         }
2874 #endif
2875
2876                         int IList.Add (object value)
2877                         {
2878                                 throw new NotSupportedException ();
2879                         }
2880
2881                         void IList.Clear ()
2882                         {
2883                                 throw new NotSupportedException ();
2884                         }
2885
2886                         void IList.Insert (int index, object value)
2887                         {
2888                                 throw new NotSupportedException ();
2889                         }
2890
2891                         void IList.Remove (object value)
2892                         {
2893                                 throw new NotSupportedException ();
2894                         }
2895
2896                         void IList.RemoveAt (int index)
2897                         {
2898                                 throw new NotSupportedException ();
2899                         }
2900         
2901                         public int IndexOf (object selectedObject)
2902                         {
2903                                 int idx = owner.items.IndexOf (selectedObject);
2904                                 return idx == -1 ? -1 : owner.selected_indices.IndexOf (idx);
2905                         }
2906
2907                         public IEnumerator GetEnumerator ()
2908                         {
2909                                 //FIXME: write an enumerator that uses selection.GetEnumerator
2910                                 //  so that invalidation is write on selection changes
2911                                 object [] items = new object [Count];
2912                                 for (int i = 0; i < Count; i++) {
2913                                         items [i] = owner.items [owner.selected_indices [i]];
2914                                 }
2915
2916                                 return items.GetEnumerator ();
2917                         }
2918
2919                         #endregion Public Methods
2920                 }
2921         }
2922 }