New test.
[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.Reflection;
36 using System.Runtime.InteropServices;
37
38 namespace System.Windows.Forms
39 {
40         [DefaultProperty("Items")]
41         [DefaultEvent("SelectedIndexChanged")]
42         [Designer ("System.Windows.Forms.Design.ListBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
43         public class ListBox : ListControl
44         {
45                 public const int DefaultItemHeight = 13;
46                 public const int NoMatches = -1;
47                 
48                 internal enum ItemNavigation
49                 {
50                         First,
51                         Last,
52                         Next,
53                         Previous,
54                         NextPage,
55                         PreviousPage,
56                         PreviousColumn,
57                         NextColumn
58                 }
59                 
60                 Hashtable item_heights;
61                 private int item_height = -1;
62                 private int column_width = 0;
63                 private int requested_height = -1;
64                 private DrawMode draw_mode = DrawMode.Normal;
65                 private int horizontal_extent = 0;
66                 private bool horizontal_scrollbar = false;
67                 private bool integral_height = true;
68                 private bool multicolumn = false;
69                 private bool scroll_always_visible = false;
70                 private int selected_index = -1;                
71                 private SelectedIndexCollection selected_indices;               
72                 private SelectedObjectCollection selected_items;
73                 private ArrayList selection = new ArrayList ();
74                 private ArrayList display_selection = new ArrayList ();
75                 private SelectionMode selection_mode = SelectionMode.One;
76                 private bool sorted = false;
77                 private bool use_tabstops = true;
78                 private int column_width_internal = 120;
79                 private ImplicitVScrollBar vscrollbar;
80                 private ImplicitHScrollBar hscrollbar;
81                 private int hbar_offset;
82                 private bool suspend_layout;
83                 private bool ctrl_pressed = false;
84                 private bool shift_pressed = false;
85                 private bool has_focus = false;
86                 private bool explicit_item_height = false;
87                 private int top_index = 0;
88                 private int last_visible_index = 0;
89                 private Rectangle items_area;
90                 private int focused_item = -1;          
91                 private ObjectCollection items;
92
93                 public ListBox ()
94                 {
95                         border_style = BorderStyle.Fixed3D;                     
96                         BackColor = ThemeEngine.Current.ColorWindow;
97
98                         items = CreateItemCollection ();
99                         selected_indices = new SelectedIndexCollection (this);
100                         selected_items = new SelectedObjectCollection (this);
101
102                         /* Vertical scrollbar */
103                         vscrollbar = new ImplicitVScrollBar ();
104                         vscrollbar.Minimum = 0;
105                         vscrollbar.SmallChange = 1;
106                         vscrollbar.LargeChange = 1;
107                         vscrollbar.Maximum = 0;
108                         vscrollbar.ValueChanged += new EventHandler (VerticalScrollEvent);
109                         vscrollbar.Visible = false;
110
111                         /* Horizontal scrollbar */
112                         hscrollbar = new ImplicitHScrollBar ();
113                         hscrollbar.Minimum = 0;
114                         hscrollbar.SmallChange = 1;
115                         hscrollbar.LargeChange = 1;
116                         hscrollbar.Maximum = 0;
117                         hscrollbar.Visible = false;
118                         hscrollbar.ValueChanged += new EventHandler (HorizontalScrollEvent);
119
120                         /* Events */
121                         MouseDown += new MouseEventHandler (OnMouseDownLB);
122                         MouseMove += new MouseEventHandler (OnMouseMoveLB);
123                         MouseUp += new MouseEventHandler (OnMouseUpLB);
124                         MouseWheel += new MouseEventHandler (OnMouseWheelLB);
125                         KeyDown += new KeyEventHandler (OnKeyDownLB);
126                         KeyUp += new KeyEventHandler (OnKeyUpLB);
127                         GotFocus += new EventHandler (OnGotFocus);
128                         LostFocus += new EventHandler (OnLostFocus);
129                         
130                         SetStyle (ControlStyles.UserPaint, false);
131                 }
132
133                 #region Events
134                 [Browsable (false)]
135                 [EditorBrowsable (EditorBrowsableState.Never)]
136                 public new event EventHandler BackgroundImageChanged {
137                         add { base.BackgroundImageChanged += value; }
138                         remove { base.BackgroundImageChanged -= value; }
139                 }
140
141                 [Browsable (false)]
142                 [EditorBrowsable (EditorBrowsableState.Advanced)]
143                 public new event EventHandler Click {
144                         add { base.Click += value; }
145                         remove { base.Click -= value; }
146                 }
147
148                 public event DrawItemEventHandler DrawItem;
149                 public event MeasureItemEventHandler MeasureItem;
150
151                 [Browsable (false)]
152                 [EditorBrowsable (EditorBrowsableState.Never)]
153                 public new event PaintEventHandler Paint {
154                         add { base.Paint += value; }
155                         remove { base.Paint -= value; }
156                 }
157
158                 public event EventHandler SelectedIndexChanged;
159
160                 [Browsable (false)]
161                 [EditorBrowsable (EditorBrowsableState.Advanced)]
162                 public new event EventHandler TextChanged {
163                         add { base.TextChanged += value; }
164                         remove { base.TextChanged -= value; }
165                 }
166                 #endregion // Events
167
168                 #region Public Properties
169                 public override Color BackColor {
170                         get { return base.BackColor; }
171                         set {
172                                 if (base.BackColor == value)
173                                         return;
174
175                                 base.BackColor = value;
176                                 base.Refresh ();        // Careful. Calling the base method is not the same that calling 
177                         }                               // the overriden one that refresh also all the items
178                 }
179
180                 [Browsable (false)]
181                 [EditorBrowsable (EditorBrowsableState.Never)]
182                 public override Image BackgroundImage {
183                         get { return base.BackgroundImage; }
184                         set { 
185                                 base.BackgroundImage = value;
186                                 base.Refresh ();
187                         }
188                 }
189
190                 [DefaultValue (BorderStyle.Fixed3D)]
191                 [DispId(-504)]
192                 public BorderStyle BorderStyle {
193                         get { return InternalBorderStyle; }
194                         set { 
195                                 InternalBorderStyle = value; 
196                                 UpdateBounds ();
197                         }
198                 }
199
200                 [DefaultValue (0)]
201                 [Localizable (true)]
202                 public int ColumnWidth {
203                         get { return column_width; }
204                         set {
205                                 if (value < 0)
206                                         throw new ArgumentException ("A value less than zero is assigned to the property.");
207
208                                 column_width = value;
209
210                                 if (value == 0)
211                                         ColumnWidthInternal = 120;
212                                 else
213                                         ColumnWidthInternal = value;
214
215                                 base.Refresh ();
216                         }
217                 }
218
219                 protected override CreateParams CreateParams {
220                         get { return base.CreateParams;}
221                 }
222
223                 protected override Size DefaultSize {
224                         get { return new Size (120, 96); }
225                 }
226
227                 [RefreshProperties(RefreshProperties.Repaint)]
228                 [DefaultValue (DrawMode.Normal)]
229                 public virtual DrawMode DrawMode {
230                         get { return draw_mode; }
231
232                         set {
233                                 if (!Enum.IsDefined (typeof (DrawMode), value))
234                                         throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for DrawMode", value));
235                                         
236                                 if (value == DrawMode.OwnerDrawVariable && multicolumn == true)
237                                         throw new ArgumentException ("Cannot have variable height and multicolumn");
238
239                                 if (draw_mode == value)
240                                         return;
241
242                                 draw_mode = value;
243
244                                 if (draw_mode == DrawMode.OwnerDrawVariable)
245                                         item_heights = new Hashtable ();
246                                 else
247                                         item_heights = null;
248
249                                 base.Refresh ();
250                         }
251                 }
252
253                 public override Color ForeColor {
254                         get { return base.ForeColor; }
255                         set {
256
257                                 if (base.ForeColor == value)
258                                         return;
259
260                                 base.ForeColor = value;
261                                 base.Refresh ();
262                         }
263                 }
264
265                 [DefaultValue (0)]
266                 [Localizable (true)]
267                 public int HorizontalExtent {
268                         get { return horizontal_extent; }
269                         set {
270                                 if (horizontal_extent == value)
271                                         return;
272
273                                 horizontal_extent = value;
274                                 base.Refresh ();
275                         }
276                 }
277
278                 [DefaultValue (false)]
279                 [Localizable (true)]
280                 public bool HorizontalScrollbar {
281                         get { return horizontal_scrollbar; }
282                         set {
283                                 if (horizontal_scrollbar == value)
284                                         return;
285
286                                 horizontal_scrollbar = value;
287                                 UpdateScrollBars ();
288                                 base.Refresh ();
289                         }
290                 }
291
292                 [DefaultValue (true)]
293                 [Localizable (true)]
294                 [RefreshProperties(RefreshProperties.Repaint)]
295                 public bool IntegralHeight {
296                         get { return integral_height; }
297                         set {
298                                 if (integral_height == value)
299                                         return;
300
301                                 integral_height = value;
302                                 UpdateBounds ();
303                         }
304                 }
305
306                 [DefaultValue (13)]
307                 [Localizable (true)]
308                 [RefreshProperties(RefreshProperties.Repaint)]
309                 public virtual int ItemHeight {
310                         get {
311                                 if (item_height == -1) {
312                                         SizeF sz = DeviceContext.MeasureString ("The quick brown Fox", Font);
313                                         item_height = (int) sz.Height;
314                                 }
315                                 return item_height;
316                         }
317                         set {
318                                 if (value > 255)
319                                         throw new ArgumentOutOfRangeException ("The ItemHeight property was set beyond 255 pixels");
320
321                                 explicit_item_height = true;
322                                 if (item_height == value)
323                                         return;
324
325                                 item_height = value;
326                                 if (IntegralHeight)
327                                         UpdateBounds ();
328                                 Layout ();
329                         }
330                 }
331
332                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
333                 [Localizable (true)]
334                 [Editor ("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
335                 public ObjectCollection Items {
336                         get { return items; }
337                 }
338
339                 [DefaultValue (false)]
340                 public bool MultiColumn {
341                         get { return multicolumn; }
342                         set {
343                                 if (multicolumn == value)
344                                         return;
345
346                                 if (value == true && DrawMode == DrawMode.OwnerDrawVariable)
347                                         throw new ArgumentException ("A multicolumn ListBox cannot have a variable-sized height.");
348                                         
349                                 multicolumn = value;
350                                 Layout ();
351                         }
352                 }
353
354                 [Browsable (false)]
355                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
356                 [EditorBrowsable (EditorBrowsableState.Advanced)]
357                 public int PreferredHeight {
358                         get {
359                                 int itemsHeight = 0;
360                                 if (draw_mode == DrawMode.Normal)
361                                         itemsHeight = FontHeight * items.Count;
362                                 else if (draw_mode == DrawMode.OwnerDrawFixed)
363                                         itemsHeight = ItemHeight * items.Count;
364                                 else if (draw_mode == DrawMode.OwnerDrawVariable) {
365                                         for (int i = 0; i < items.Count; i++)
366                                                 itemsHeight += (int) item_heights [Items [i]];
367                                 }
368                                 
369                                 return itemsHeight;
370                         }
371                 }
372
373                 public override RightToLeft RightToLeft {
374                         get { return base.RightToLeft; }
375                         set {
376                                 base.RightToLeft = value;                               
377                                 if (base.RightToLeft == RightToLeft.Yes)
378                                         StringFormat.Alignment = StringAlignment.Far;                           
379                                 else
380                                         StringFormat.Alignment = StringAlignment.Near;                          
381                                 base.Refresh ();
382                         }
383                 }
384
385                 // Only affects the Vertical ScrollBar
386                 [DefaultValue (false)]
387                 [Localizable (true)]
388                 public bool ScrollAlwaysVisible {
389                         get { return scroll_always_visible; }
390                         set {
391                                 if (scroll_always_visible == value)
392                                         return;
393
394                                 scroll_always_visible = value;
395                                 UpdateScrollBars ();
396                         }
397                 }
398
399                 [Bindable(true)]
400                 [Browsable (false)]
401                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
402                 public override int SelectedIndex {
403                         get { return selected_index;}
404                         set {
405                                 if (value < -1 || value >= Items.Count)
406                                         throw new ArgumentOutOfRangeException ("Index of out range");
407
408                                 if (SelectionMode == SelectionMode.None)
409                                         throw new ArgumentException ("cannot call this method if SelectionMode is SelectionMode.None");
410
411                                 if (selected_index == value)
412                                         return;
413
414                                 if (value == -1)
415                                         ClearSelected ();
416                                 else if (SelectionMode == SelectionMode.One)
417                                         UnSelectItem (selected_index, true);
418
419                                 SelectItem (value);
420                                 selected_index = value;
421                                 FocusedItem = value;
422                                 OnSelectedIndexChanged  (new EventArgs ());
423                                 OnSelectedValueChanged (new EventArgs ());
424                         }
425                 }
426
427                 [Browsable (false)]
428                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
429                 public SelectedIndexCollection SelectedIndices {
430                         get { return selected_indices; }
431                 }
432
433                 [Bindable(true)]
434                 [Browsable (false)]
435                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
436                 public object SelectedItem {
437                         get {
438                                 if (SelectedItems.Count > 0)
439                                         return SelectedItems[0];
440                                 else
441                                         return null;
442                         }
443                         set {
444                                 if (value != null && !Items.Contains (value))
445                                         return; // FIXME: this is probably an exception
446                                         
447                                 SelectedIndex = value == null ? - 1 : Items.IndexOf (value);
448                         }
449                 }
450
451                 [Browsable (false)]
452                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
453                 public SelectedObjectCollection SelectedItems {
454                         get {return selected_items;}
455                 }
456
457                 [DefaultValue (SelectionMode.One)]
458                 public virtual SelectionMode SelectionMode {
459                         get { return selection_mode; }
460                         set {
461                                 if (!Enum.IsDefined (typeof (SelectionMode), value))
462                                         throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for SelectionMode", value));
463
464                                 if (selection_mode == value)
465                                         return;
466                                         
467                                 selection_mode = value;
468                                         
469                                 switch (selection_mode) {
470                                 case SelectionMode.None: 
471                                         ClearSelected ();
472                                         break;                                          
473
474                                 case SelectionMode.One:
475                                         while (SelectedIndices.Count > 1)
476                                                 UnSelectItem (SelectedIndices [SelectedIndices.Count - 1], true);
477                                         break;
478
479                                 default:
480                                         break;                                          
481                                 }
482                         }
483                 }
484
485                 [DefaultValue (false)]
486                 public bool Sorted {
487                         get { return sorted; }
488
489                         set {
490                                 if (sorted == value)
491                                         return;
492
493                                 sorted = value;
494                                 if (sorted)
495                                         Sort ();
496                         }
497                 }
498
499                 [Bindable (false)]
500                 [Browsable (false)]
501                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
502                 [EditorBrowsable (EditorBrowsableState.Advanced)]
503                 public override string Text {
504                         get {
505                                 if (SelectionMode != SelectionMode.None && SelectedIndex != -1)
506                                         return GetItemText (SelectedItem);
507
508                                 return base.Text;
509                         }
510                         set {
511
512                                 base.Text = value;
513
514                                 if (SelectionMode == SelectionMode.None)
515                                         return;
516
517                                 int index;
518
519                                 index = FindStringExact (value);
520
521                                 if (index == -1)
522                                         return;
523
524                                 SelectedIndex = index;
525                         }
526                 }
527
528                 [Browsable (false)]
529                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
530                 public int TopIndex {
531                         get { return top_index; }
532                         set {
533                                 if (value == top_index)
534                                         return;
535
536                                 if (value < 0 || value >= Items.Count)
537                                         return;
538
539                                 top_index = value;
540                                 UpdateTopItem ();
541                                 base.Refresh ();
542                         }
543                 }
544
545                 [DefaultValue (true)]
546                 public bool UseTabStops {
547                         get { return use_tabstops; }
548
549                         set {
550                                 if (use_tabstops == value)
551                                         return;
552
553                                 use_tabstops = value;                                   
554                                 if (use_tabstops)
555                                         StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
556                                 else
557                                         StringFormat.SetTabStops (0, new float [0]);
558                                 base.Refresh ();
559                         }
560                 }
561
562                 #endregion Public Properties
563
564                 #region Private Properties
565
566                 private int ColumnWidthInternal {
567                         get { return column_width_internal; }
568                         set { column_width_internal = value; }
569                 }
570
571                 private int row_count = 1;
572                 private int RowCount {
573                         get {
574                                 return MultiColumn ? row_count : Items.Count;
575                         }
576                 }
577
578                 #endregion Private Properties
579
580                 #region Public Methods
581                 protected virtual void AddItemsCore (object[] value)
582                 {
583                         Items.AddRange (value);
584                 }
585
586                 public void BeginUpdate ()
587                 {
588                         suspend_layout = true;
589                 }
590
591                 public void ClearSelected ()
592                 {
593                         foreach (int i in selected_indices) {
594                                 UnSelectItem (i, false);
595                         }
596
597                         selection.Clear ();
598                 }
599
600                 protected virtual ObjectCollection CreateItemCollection ()
601                 {
602                         return new ObjectCollection (this);
603                 }
604
605                 public void EndUpdate ()
606                 {
607                         suspend_layout = false;
608                         Layout ();
609                         base.Refresh ();
610                 }
611
612                 public int FindString (String s)
613                 {
614                         return FindString (s, -1);
615                 }
616
617                 public int FindString (string s,  int startIndex)
618                 {
619                         if (Items.Count == 0)
620                                 return -1; // No exception throwing if empty
621
622                         if (startIndex < -1 || startIndex >= Items.Count - 1)
623                                 throw new ArgumentOutOfRangeException ("Index of out range");
624
625                         startIndex++;
626                         for (int i = startIndex; i < Items.Count; i++) {
627                                 if ((GetItemText (Items[i])).StartsWith (s))
628                                         return i;
629                         }
630
631                         return NoMatches;
632                 }
633
634                 public int FindStringExact (string s)
635                 {
636                         return FindStringExact (s, -1);
637                 }
638
639                 public int FindStringExact (string s,  int startIndex)
640                 {
641                         if (Items.Count == 0)
642                                 return -1; // No exception throwing if empty
643
644                         if (startIndex < -1 || startIndex >= Items.Count - 1)
645                                 throw new ArgumentOutOfRangeException ("Index of out range");
646
647                         startIndex++;
648                         for (int i = startIndex; i < Items.Count; i++) {
649                                 if ((GetItemText (Items[i])).Equals (s))
650                                         return i;
651                         }
652
653                         return NoMatches;
654                 }
655
656                 public int GetItemHeight (int index)
657                 {
658                         if (index < 0 || index >= Items.Count)
659                                 throw new ArgumentOutOfRangeException ("Index of out range");
660                                 
661                         if (DrawMode == DrawMode.OwnerDrawVariable && IsHandleCreated == true) {
662                                 
663                                 object o = Items [index];
664                                 if (item_heights.Contains (o))
665                                         return (int) item_heights [o];
666                                 
667                                 MeasureItemEventArgs args = new MeasureItemEventArgs (DeviceContext, index, ItemHeight);
668                                 OnMeasureItem (args);
669                                 item_heights [o] = args.ItemHeight;
670                                 return args.ItemHeight;
671                         }
672
673                         return ItemHeight;
674                 }
675
676                 public Rectangle GetItemRectangle (int index)
677                 {
678                         if (index < 0 || index >= Items.Count)
679                                 throw new  ArgumentOutOfRangeException ("GetItemRectangle index out of range.");
680
681                         Rectangle rect = new Rectangle ();
682
683                         if (MultiColumn) {
684                                 int col = index / RowCount;
685                                 rect.Y = ((index - top_index) % RowCount) * ItemHeight;
686                                 rect.X = col * ColumnWidthInternal;
687                                 rect.Height = ItemHeight;
688                                 rect.Width = ColumnWidthInternal;
689                         } else {
690                                 rect.X = 0;                             
691                                 rect.Height = GetItemHeight (index);
692                                 rect.Width = items_area.Width;
693                                 
694                                 if (DrawMode == DrawMode.OwnerDrawVariable) {
695                                         rect.Y = 0;
696                                         if (index >= top_index) {
697                                                 for (int i = top_index; i < index; i++) {
698                                                         rect.Y += GetItemHeight (i);
699                                                 }
700                                         } else {
701                                                 for (int i = index; i < top_index; i++) {
702                                                         rect.Y -= GetItemHeight (i);
703                                                 }
704                                         }
705                                 } else {
706                                         rect.Y = ItemHeight * (index - top_index);      
707                                 }                               
708                         }
709
710                         return rect;
711                 }
712
713                 public bool GetSelected (int index)
714                 {
715                         if (index < 0 || index >= Items.Count)
716                                 throw new ArgumentOutOfRangeException ("Index of out range");
717
718                         return SelectedIndices.Contains (index);
719                 }
720
721                 public int IndexFromPoint (Point p)
722                 {
723                         return IndexFromPoint (p.X, p.Y);
724                 }
725
726                 // Only returns visible points
727                 public int IndexFromPoint (int x, int y)
728                 {
729
730                         if (Items.Count == 0) {
731                                 return -1;
732                         }
733
734                         for (int i = top_index; i <= last_visible_index; i++) {
735                                 if (GetItemRectangle (i).Contains (x,y) == true)
736                                         return i;
737                         }
738
739                         return -1;
740                 }
741
742                 protected override void OnChangeUICues (UICuesEventArgs e)
743                 {
744                         base.OnChangeUICues (e);
745                 }
746
747                 protected override void OnDataSourceChanged (EventArgs e)
748                 {
749                         base.OnDataSourceChanged (e);
750                         BindDataItems ();
751                         
752                         if (DataSource == null || DataManager == null) {
753                                 SelectedIndex = -1;
754                         } 
755                         else {
756                                 SelectedIndex = DataManager.Position;
757                         }
758                 }
759
760                 protected override void OnDisplayMemberChanged (EventArgs e)
761                 {
762                         base.OnDisplayMemberChanged (e);
763
764                         if (DataManager == null || !IsHandleCreated)
765                                 return;
766
767                         BindDataItems ();
768                         base.Refresh ();
769                 }
770
771                 protected virtual void OnDrawItem (DrawItemEventArgs e)
772                 {                       
773                         switch (DrawMode) {
774                         case DrawMode.OwnerDrawFixed:
775                         case DrawMode.OwnerDrawVariable:
776                                 if (DrawItem != null)
777                                         DrawItem (this, e);
778                                 break;
779
780                         default:
781                                 ThemeEngine.Current.DrawListBoxItem (this, e);
782                                 break;
783                         }
784                 }
785
786                 protected override void OnFontChanged (EventArgs e)
787                 {
788                         base.OnFontChanged (e);
789
790                         if (use_tabstops)
791                                 StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
792
793                         if (explicit_item_height) {
794                                 base.Refresh ();
795                         } else {
796                                 SizeF sz = DeviceContext.MeasureString ("The quick brown Fox", Font);
797                                 item_height = (int) sz.Height;
798                                 if (IntegralHeight)
799                                         UpdateBounds ();
800                                 Layout ();
801                         }
802                 }
803
804                 protected override void OnHandleCreated (EventArgs e)
805                 {
806                         base.OnHandleCreated (e);
807
808                         SuspendLayout ();
809                         Controls.AddImplicit (vscrollbar);
810                         Controls.AddImplicit (hscrollbar);
811                         ResumeLayout ();
812                         Layout ();
813                 }
814
815                 protected override void OnHandleDestroyed (EventArgs e)
816                 {
817                         base.OnHandleDestroyed (e);
818                 }
819
820                 protected virtual void OnMeasureItem (MeasureItemEventArgs e)
821                 {
822                         if (draw_mode != DrawMode.OwnerDrawVariable)
823                                 return;
824                                 
825                         if (MeasureItem != null)
826                                 MeasureItem (this, e);
827                 }
828
829                 protected override void OnParentChanged (EventArgs e)
830                 {
831                         base.OnParentChanged (e);
832                 }
833
834                 protected override void OnResize (EventArgs e)
835                 {
836                         base.OnResize (e);
837                         if (canvas_size.IsEmpty || MultiColumn)
838                                 Layout ();
839                 }
840
841                 protected override void OnSelectedIndexChanged (EventArgs e)
842                 {
843                         base.OnSelectedIndexChanged (e);
844
845                         if (SelectedIndexChanged != null)
846                                 SelectedIndexChanged (this, e);
847                 }
848
849                 protected override void OnSelectedValueChanged (EventArgs e)
850                 {
851                         base.OnSelectedValueChanged (e);
852                 }
853
854                 public override void Refresh ()
855                 {
856                         if (draw_mode == DrawMode.OwnerDrawVariable)
857                                 item_heights.Clear ();
858                         
859                         base.Refresh ();
860                 }
861
862                 protected override void RefreshItem (int index)
863                 {
864                         if (index < 0 || index >= Items.Count)
865                                 throw new ArgumentOutOfRangeException ("Index of out range");
866                                 
867                         if (draw_mode == DrawMode.OwnerDrawVariable)
868                                 item_heights.Remove (Items [index]);
869                 }
870
871                 protected override void SetBoundsCore (int x,  int y, int width, int height, BoundsSpecified specified)
872                 {
873                         if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height)
874                                 requested_height = height;
875
876                         if (IntegralHeight) {
877                                 int border;
878                                 switch (border_style) {
879                                 case BorderStyle.Fixed3D:
880                                         border = ThemeEngine.Current.Border3DSize.Height;
881                                         break;
882                                 case BorderStyle.FixedSingle:
883                                         border = ThemeEngine.Current.BorderSize.Height;
884                                         break;
885                                 case BorderStyle.None:
886                                 default:
887                                         border = 0;
888                                         break;
889                                 }
890                                 height -= (2 * border);
891                                 height -= height % ItemHeight;
892                                 height += (2 * border);
893                         }
894
895                         base.SetBoundsCore (x, y, width, height, specified);
896                         UpdateScrollBars ();
897                 }
898
899                 protected override void SetItemCore (int index,  object value)
900                 {
901                         if (index < 0 || index >= Items.Count)
902                                 return;
903
904                         Items[index] = value;
905                 }
906
907                 protected override void SetItemsCore (IList value)
908                 {
909                         BeginUpdate ();
910                         try {
911                                 Items.Clear ();
912                                 Items.AddRange (value);
913                         } finally {
914                                 EndUpdate ();
915                         }
916                 }
917
918                 public void SetSelected (int index, bool value)
919                 {
920                         if (index < 0 || index >= Items.Count)
921                                 throw new ArgumentOutOfRangeException ("Index of out range");
922
923                         if (SelectionMode == SelectionMode.None)
924                                 throw new InvalidOperationException ();
925
926                         if (value)
927                                 SelectItem (index);
928                         else
929                                 UnSelectItem (index, true);
930                 }
931
932                 protected virtual void Sort ()
933                 {
934                         if (Items.Count == 0)
935                                 return;
936
937                         Items.Sort ();
938                         base.Refresh ();
939                 }
940
941                 public override string ToString ()
942                 {
943                         return base.ToString ();
944                 }
945
946                 protected virtual void WmReflectCommand (ref Message m)
947                 {
948                 }
949
950                 protected override void WndProc (ref Message m)
951                 {
952                         base.WndProc (ref m);
953                 }
954
955                 #endregion Public Methods
956
957                 #region Private Methods
958
959                 private Size canvas_size;
960
961                 private void Layout ()
962                 {
963                         if (!IsHandleCreated || suspend_layout)
964                                 return;
965
966                         if (MultiColumn)
967                                 LayoutMultiColumn ();
968                         else
969                                 LayoutSingleColumn ();
970
971                         UpdateScrollBars ();
972                         last_visible_index = LastVisibleItem ();
973                 }
974
975                 private void LayoutSingleColumn ()
976                 {
977                         int height, width;
978
979                         switch (DrawMode) {
980                         case DrawMode.OwnerDrawVariable:
981                                 height = 0;
982                                 width = HorizontalExtent;
983                                 for (int i = 0; i < Items.Count; i++) {
984                                         height += GetItemHeight (i);
985                                 }
986                                 break;
987
988                         case DrawMode.OwnerDrawFixed:
989                                 height = Items.Count * ItemHeight;
990                                 width = HorizontalExtent;
991                                 break;
992
993                         case DrawMode.Normal:
994                         default:
995                                 height = Items.Count * ItemHeight;
996                                 width = 0;
997                                 for (int i = 0; i < Items.Count; i++) {
998                                         SizeF sz = DeviceContext.MeasureString (GetItemText (Items[i]), Font);
999                                         if ((int) sz.Width > width)
1000                                                 width = (int) sz.Width;
1001                                 }
1002                                 break;
1003                         }
1004
1005                         canvas_size = new Size (width, height);
1006                 }
1007
1008                 private void LayoutMultiColumn ()
1009                 {
1010                         int usable_height = ClientRectangle.Height - (ScrollAlwaysVisible ? hscrollbar.Height : 0);
1011                         row_count = usable_height / ItemHeight;
1012                         if (row_count == 0)
1013                                 row_count = 1;
1014                         int cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1015                         Size sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1016                         if (!ScrollAlwaysVisible && sz.Width > ClientRectangle.Width && row_count > 1) {
1017                                 usable_height = ClientRectangle.Height - hscrollbar.Height;
1018                                 row_count = usable_height / ItemHeight;
1019                                 cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1020                                 sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1021                         }
1022                         canvas_size = sz;
1023                 }
1024
1025                 internal void Draw (Rectangle clip, Graphics dc)
1026                 {                               
1027                         Theme theme = ThemeEngine.Current;
1028
1029                         if (hscrollbar.Visible && vscrollbar.Visible) {
1030                                 // Paint the dead space in the bottom right corner
1031                                 Rectangle rect = new Rectangle (hscrollbar.Right, vscrollbar.Bottom, vscrollbar.Width, hscrollbar.Height);
1032                                 if (rect.IntersectsWith (clip))
1033                                         dc.FillRectangle (theme.ResPool.GetSolidBrush (theme.ColorControl), rect);
1034                         }
1035
1036                         dc.FillRectangle (theme.ResPool.GetSolidBrush (BackColor), items_area);
1037
1038                         if (Items.Count == 0)
1039                                 return;
1040
1041                         for (int i = top_index; i <= last_visible_index; i++) {
1042                                 Rectangle rect = GetItemDisplayRectangle (i, top_index);
1043
1044                                 if (!clip.IntersectsWith (rect))
1045                                         continue;
1046
1047                                 DrawItemState state = DrawItemState.None;
1048
1049                                 if (SelectedIndices.Contains (i))
1050                                         state |= DrawItemState.Selected;
1051                                         
1052                                 if (has_focus && FocusedItem == i)
1053                                         state |= DrawItemState.Focus;
1054                                         
1055                                 if (MultiColumn == false && hscrollbar != null && hscrollbar.Visible) {
1056                                         rect.X -= hscrollbar.Value;
1057                                         rect.Width += hscrollbar.Value;
1058                                 }
1059
1060                                 OnDrawItem (new DrawItemEventArgs (dc, Font, rect, i, state, ForeColor, BackColor));
1061                         }
1062                 }
1063
1064                 // Converts a GetItemRectangle to a one that we can display
1065                 internal Rectangle GetItemDisplayRectangle (int index, int first_displayble)
1066                 {
1067                         Rectangle item_rect;
1068                         Rectangle first_item_rect = GetItemRectangle (first_displayble);
1069                         item_rect = GetItemRectangle (index);
1070                         item_rect.X -= first_item_rect.X;
1071                         item_rect.Y -= first_item_rect.Y;
1072                         
1073                         return item_rect;
1074                 }
1075
1076                 // Value Changed
1077                 private void HorizontalScrollEvent (object sender, EventArgs e)
1078                 {
1079                         if (multicolumn) {
1080                                 int top_item = top_index;
1081                                 int last_item = last_visible_index;
1082
1083                                 top_index = RowCount * hscrollbar.Value;
1084                                 last_visible_index = LastVisibleItem ();
1085
1086                                 if (top_item != top_index || last_item != last_visible_index)
1087                                         Invalidate (items_area);
1088                         }
1089                         else {
1090                                 int old_offset = hbar_offset;
1091                                 hbar_offset = hscrollbar.Value;
1092
1093                                 if (hbar_offset < 0)
1094                                         hbar_offset = 0;
1095
1096                                 XplatUI.ScrollWindow (Handle, items_area, old_offset - hbar_offset, 0, false);
1097                         }
1098                 }
1099
1100                 // Only returns visible points. The diference of with IndexFromPoint is that the rectangle
1101                 // has screen coordinates
1102                 private int IndexAtClientPoint (int x, int y)
1103                 {       
1104                         if (Items.Count == 0)
1105                                 return -1;
1106                         
1107                         if (x < 0)
1108                                 x = 0;
1109                         else if (x > ClientRectangle.Right)
1110                                 x = ClientRectangle.Right;
1111
1112                         if (y < 0)
1113                                 y = 0;
1114                         else if (y > ClientRectangle.Bottom)
1115                                 y = ClientRectangle.Bottom;
1116
1117                         for (int i = top_index; i <= last_visible_index; i++)
1118                                 if (GetItemDisplayRectangle (i, top_index).Contains (x, y))
1119                                         return i;
1120
1121                         return -1;
1122                 }
1123
1124                 private int LastVisibleItem ()
1125                 {
1126                         Rectangle item_rect;
1127                         int top_y = items_area.Y + items_area.Height;
1128                         int i = 0;
1129
1130                         if (top_index >= Items.Count)
1131                                 return top_index;
1132
1133                         for (i = top_index; i < Items.Count; i++) {
1134
1135                                 item_rect = GetItemDisplayRectangle (i, top_index);
1136                                 if (MultiColumn) {
1137
1138                                         if (item_rect.X > items_area.Width)
1139                                                 return i - 1;
1140                                 }
1141                                 else {                                  
1142                                         if (item_rect.Y + item_rect.Height > top_y) {
1143                                                 return i;
1144                                         }
1145                                 }
1146                         }
1147                         return i - 1;
1148                 }
1149
1150                 private void UpdateTopItem ()
1151                 {
1152                         if (MultiColumn) {                              
1153                                 int col = top_index / RowCount;
1154                                 
1155                                 if (col > hscrollbar.Maximum)
1156                                         hscrollbar.Value = hscrollbar.Maximum;
1157                                 else
1158                                         hscrollbar.Value = col;
1159                         } else {
1160                                 int val = vscrollbar.Value;
1161                                 if (top_index > vscrollbar.Maximum)
1162                                         vscrollbar.Value = vscrollbar.Maximum;
1163                                 else
1164                                         vscrollbar.Value = top_index;
1165                                 Scroll (vscrollbar, vscrollbar.Value - top_index);
1166                                 XplatUI.ScrollWindow (Handle, items_area, 0, ItemHeight * (val - vscrollbar.Value), false);
1167                         }
1168                 }
1169                 
1170                 // Navigates to the indicated item and returns the new item
1171                 private int NavigateItemVisually (ItemNavigation navigation)
1172                 {                       
1173                         int page_size, columns, selected_index = -1;
1174
1175                         if (multicolumn) {
1176                                 columns = items_area.Width / ColumnWidthInternal; 
1177                                 page_size = columns * RowCount;
1178                                 if (page_size == 0) {
1179                                         page_size = RowCount;
1180                                 }
1181                         } else {
1182                                 page_size = items_area.Height / ItemHeight;     
1183                         }
1184
1185                         switch (navigation) {
1186
1187                         case ItemNavigation.PreviousColumn: {
1188                                 if (FocusedItem - RowCount < 0) {
1189                                         return -1;
1190                                 }
1191
1192                                 if (FocusedItem - RowCount < top_index) {
1193                                         top_index = FocusedItem - RowCount;
1194                                         UpdateTopItem ();
1195                                 }
1196                                         
1197                                 selected_index = FocusedItem - RowCount;
1198                                 break;
1199                         }
1200                         
1201                         case ItemNavigation.NextColumn: {
1202                                 if (FocusedItem + RowCount >= Items.Count) {
1203                                         break;
1204                                 }
1205
1206                                 if (FocusedItem + RowCount > last_visible_index) {
1207                                         top_index = FocusedItem;
1208                                         UpdateTopItem ();
1209                                 }
1210                                         
1211                                 selected_index = FocusedItem + RowCount;                                        
1212                                 break;
1213                         }
1214
1215                         case ItemNavigation.First: {
1216                                 top_index = 0;
1217                                 selected_index  = 0;
1218                                 UpdateTopItem ();
1219                                 break;
1220                         }
1221
1222                         case ItemNavigation.Last: {
1223
1224                                 int rows = items_area.Height / ItemHeight;
1225                                 if (Items.Count < rows) {
1226                                         top_index = 0;
1227                                         selected_index  = Items.Count - 1;
1228                                         UpdateTopItem ();
1229                                 } else {
1230                                         top_index = Items.Count - rows;
1231                                         selected_index  = Items.Count - 1;
1232                                         UpdateTopItem ();
1233                                 }
1234                                 break;
1235                         }
1236
1237                         case ItemNavigation.Next: {
1238                                 if (FocusedItem == Items.Count - 1)
1239                                         return -1;
1240
1241                                 int bottom = 0;
1242                                 ArrayList heights = new ArrayList ();
1243                                 if (draw_mode == DrawMode.OwnerDrawVariable) {
1244                                         for (int i = top_index; i <= FocusedItem + 1; i++) {
1245                                                 int h = GetItemHeight (i);
1246                                                 bottom += h;
1247                                                 heights.Add (h);
1248                                         }
1249                                 } else {
1250                                         bottom = ((FocusedItem + 1) - top_index + 1) * ItemHeight;
1251                                 }
1252
1253                                 if (bottom >= items_area.Height) {
1254                                         int overhang = bottom - items_area.Height;
1255
1256                                         int offset = 0;
1257                                         if (draw_mode == DrawMode.OwnerDrawVariable)
1258                                                 while (overhang > 0)
1259                                                         overhang -= (int) heights [offset];
1260                                         else
1261                                                 offset = (int) Math.Ceiling ((float)overhang / (float) ItemHeight);
1262                                         top_index += offset;
1263                                         UpdateTopItem ();                                               
1264                                 }
1265                                 selected_index = FocusedItem + 1;
1266                                 break;
1267                         }
1268
1269                         case ItemNavigation.Previous: {
1270                                 if (FocusedItem > 0) {
1271                                         if (FocusedItem - 1 < top_index) {
1272                                                 top_index--;
1273                                                 UpdateTopItem ();
1274                                         }
1275                                         selected_index = FocusedItem - 1;
1276                                 }                                       
1277                                 break;
1278                         }
1279
1280                         case ItemNavigation.NextPage: {
1281                                 if (Items.Count < page_size) {
1282                                         NavigateItemVisually (ItemNavigation.Last);
1283                                         break;
1284                                 }
1285
1286                                 if (FocusedItem + page_size - 1 >= Items.Count) {
1287                                         top_index = Items.Count - page_size;
1288                                         UpdateTopItem ();
1289                                         selected_index = Items.Count - 1;                                               
1290                                 }
1291                                 else {
1292                                         if (FocusedItem + page_size - 1  > last_visible_index) {
1293                                                 top_index = FocusedItem;
1294                                                 UpdateTopItem ();
1295                                         }
1296                                         
1297                                         selected_index = FocusedItem + page_size - 1;                                           
1298                                 }
1299                                         
1300                                 break;
1301                         }                       
1302
1303                         case ItemNavigation.PreviousPage: {
1304                                         
1305                                 int rows = items_area.Height / ItemHeight;
1306                                 if (FocusedItem - (rows - 1) <= 0) {
1307                                                                                                                                                 
1308                                         top_index = 0;                                  
1309                                         UpdateTopItem ();                                       
1310                                         SelectedIndex = 0;                                      
1311                                 }
1312                                 else { 
1313                                         if (FocusedItem - (rows - 1)  < top_index) {
1314                                                 top_index = FocusedItem - (rows - 1);
1315                                                 UpdateTopItem ();                                               
1316                                         }
1317                                         
1318                                         selected_index = FocusedItem - (rows - 1);
1319                                 }
1320                                         
1321                                 break;
1322                         }               
1323                         default:
1324                                 break;                          
1325                         }
1326                         
1327                         return selected_index;
1328                 }
1329                 
1330                 
1331                 private void OnGotFocus (object sender, EventArgs e)                    
1332                 {                       
1333                         has_focus = true;                       
1334                         
1335                         if (FocusedItem != -1)
1336                                 InvalidateItem (FocusedItem);
1337                 }               
1338                 
1339                 private void OnLostFocus (object sender, EventArgs e)                   
1340                 {                       
1341                         has_focus = false;
1342                         
1343                         if (FocusedItem != -1)
1344                                 InvalidateItem (FocusedItem);
1345                 }               
1346
1347                 private void OnKeyDownLB (object sender, KeyEventArgs e)
1348                 {                                       
1349                         int new_item = -1;
1350                         
1351                         if (Items.Count == 0)
1352                                 return;
1353
1354                         switch (e.KeyCode) {
1355                                 
1356                                 case Keys.ControlKey:
1357                                         ctrl_pressed = true;
1358                                         break;
1359                                         
1360                                 case Keys.ShiftKey:
1361                                         shift_pressed = true;
1362                                         break;
1363                                         
1364                                 case Keys.Home:
1365                                         new_item = NavigateItemVisually (ItemNavigation.First);
1366                                         break;  
1367
1368                                 case Keys.End:
1369                                         new_item = NavigateItemVisually (ItemNavigation.Last);
1370                                         break;  
1371
1372                                 case Keys.Up:
1373                                         new_item = NavigateItemVisually (ItemNavigation.Previous);
1374                                         break;                          
1375         
1376                                 case Keys.Down:                         
1377                                         new_item = NavigateItemVisually (ItemNavigation.Next);
1378                                         break;
1379                                 
1380                                 case Keys.PageUp:
1381                                         new_item = NavigateItemVisually (ItemNavigation.PreviousPage);
1382                                         break;                          
1383         
1384                                 case Keys.PageDown:                             
1385                                         new_item = NavigateItemVisually (ItemNavigation.NextPage);
1386                                         break;
1387
1388                                 case Keys.Right:
1389                                         if (multicolumn == true) {
1390                                                 new_item = NavigateItemVisually (ItemNavigation.NextColumn);
1391                                         }
1392                                         break;                          
1393         
1394                                 case Keys.Left:                 
1395                                         if (multicolumn == true) {      
1396                                                 new_item = NavigateItemVisually (ItemNavigation.PreviousColumn);
1397                                         }
1398                                         break;
1399                                         
1400                                 case Keys.Space:
1401                                         if (selection_mode == SelectionMode.MultiSimple) {
1402                                                 SelectedItemFromNavigation (FocusedItem);
1403                                         }
1404                                         break;
1405                                 
1406
1407                                 default:
1408                                         break;
1409                                 }
1410                                 
1411                                 if (new_item != -1) {
1412                                         FocusedItem = new_item;
1413                                         if (selection_mode != SelectionMode.MultiSimple && selection_mode != SelectionMode.None) {
1414                                                 SelectedItemFromNavigation (new_item);
1415                                         }
1416                                 }
1417                 }
1418                 
1419                 private void OnKeyUpLB (object sender, KeyEventArgs e)                  
1420                 {
1421                         switch (e.KeyCode) {
1422                                 case Keys.ControlKey:
1423                                         ctrl_pressed = false;
1424                                         break;
1425                                 case Keys.ShiftKey:
1426                                         shift_pressed = false;
1427                                         break;
1428                                 default: 
1429                                         break;
1430                         }
1431                 }               
1432
1433                 internal void InvalidateItem (int index)
1434                 {
1435                         Rectangle bounds = GetItemDisplayRectangle (index, top_index);
1436                         if (ClientRectangle.IntersectsWith (bounds))
1437                                 Invalidate (bounds);
1438                 }
1439
1440                 internal virtual void OnItemClick (int index)
1441                 {
1442                         OnSelectedIndexChanged  (EventArgs.Empty);
1443                         OnSelectedValueChanged (EventArgs.Empty);
1444                 }
1445
1446                 int anchor = -1;
1447                 int[] prev_selection;
1448                 bool button_pressed = false;
1449
1450                 private void SelectExtended (int index)
1451                 {
1452                         SuspendLayout ();
1453
1454                         ArrayList new_selection = new ArrayList ();
1455                         int start = anchor < index ? anchor : index;
1456                         int end = anchor > index ? anchor : index;
1457                         for (int i = start; i <= end; i++)
1458                                 new_selection.Add (i);
1459
1460                         if (ctrl_pressed)
1461                                 foreach (int i in prev_selection)
1462                                         if (!selection.Contains (i))
1463                                                 new_selection.Add (i);
1464
1465                         foreach (int i in SelectedIndices)
1466                                 if (!new_selection.Contains (i))
1467                                         UnSelectItem (i, true);
1468
1469                         foreach (int i in new_selection)
1470                                 if (!SelectedIndices.Contains (i))
1471                                         SelectItem (i);
1472                         ResumeLayout ();
1473                 }
1474
1475                 private void OnMouseDownLB (object sender, MouseEventArgs e)
1476                 {
1477                         int index = IndexAtClientPoint (e.X, e.Y);
1478                                                         
1479                         if (index == -1)
1480                                 return;                 
1481
1482                         switch (SelectionMode) {
1483                         case SelectionMode.One:
1484                                 if (SelectedIndex != index) {
1485                                         UnSelectItem (SelectedIndex, true);
1486                                         SelectItem (index);
1487                                 }
1488                                 selected_index = index;
1489                                 break;
1490
1491                         case SelectionMode.MultiSimple:
1492                                 if (SelectedIndices.Contains (index))
1493                                         UnSelectItem (index, true);
1494                                 else
1495                                         SelectItem (index);
1496                                 break;
1497
1498                         case SelectionMode.MultiExtended:
1499                                 shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1500                                 ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1501
1502                                 if (ctrl_pressed) {
1503                                         prev_selection = new int [selection.Count];
1504                                         SelectedIndices.CopyTo (prev_selection, 0);
1505                                 } else
1506                                         ClearSelected ();
1507
1508                                 if (!shift_pressed)
1509                                         anchor = index;
1510
1511                                 SelectExtended (index);
1512                                 break;
1513
1514                         case SelectionMode.None:
1515                         default:
1516                                 return;
1517                         }
1518                                 
1519                         button_pressed = true;
1520                         FocusedItem = index;
1521                 }
1522
1523                 private void OnMouseMoveLB (object sender, MouseEventArgs e)
1524                 {
1525                         if (!button_pressed)
1526                                 return;
1527
1528                         int index = IndexAtClientPoint (e.X, e.Y);
1529
1530                         switch (SelectionMode) {
1531                         case SelectionMode.One:
1532                                 if (index == selected_index)
1533                                         return;
1534
1535                                 UnSelectItem (SelectedIndex, true);
1536                                 SelectItem (index);
1537                                 selected_index = index;
1538                                 break;
1539
1540                         case SelectionMode.MultiSimple:
1541                                 break;
1542
1543                         case SelectionMode.MultiExtended:
1544                                 SelectExtended (index);
1545                                 break;
1546
1547                         case SelectionMode.None:
1548                         default:
1549                                 return;
1550                         }
1551
1552                         FocusedItem = index;
1553                 }
1554
1555                 private void OnMouseUpLB (object sender, MouseEventArgs e)
1556                 {
1557                         if (e.Clicks > 1)
1558                                 OnDoubleClick (EventArgs.Empty);
1559                         else if (e.Clicks == 1)
1560                                 OnClick (EventArgs.Empty);
1561                         
1562                         if (!button_pressed)
1563                                 return;
1564
1565                         int index = IndexAtClientPoint (e.X, e.Y);
1566                         OnItemClick (index);
1567                         button_pressed = ctrl_pressed = shift_pressed = false;
1568                 }
1569
1570                 private void Scroll (ScrollBar scrollbar, int delta)
1571                 {
1572                         if (delta == 0 || !scrollbar.Visible || !scrollbar.Enabled)
1573                                 return;
1574
1575                         int max;
1576                         if (scrollbar == hscrollbar)
1577                                 max = hscrollbar.Maximum - (items_area.Width / ColumnWidthInternal) + 1;
1578                         else
1579                                 max = vscrollbar.Maximum - (items_area.Height / ItemHeight) + 1;
1580
1581                         int val = scrollbar.Value + delta;
1582                         if (val > max)
1583                                 val = max;
1584                         else if (val < scrollbar.Minimum)
1585                                 val = scrollbar.Minimum;
1586                         scrollbar.Value = val;
1587                 }
1588
1589                 private void OnMouseWheelLB (object sender, MouseEventArgs me)
1590                 {
1591                         if (Items.Count == 0)
1592                                 return;
1593
1594                         int lines = me.Delta / 120;
1595
1596                         if (MultiColumn)
1597                                 Scroll (hscrollbar, -SystemInformation.MouseWheelScrollLines * lines);
1598                         else
1599                                 Scroll (vscrollbar, -lines);
1600                 }
1601
1602                 internal override void OnPaintInternal (PaintEventArgs pevent)
1603                 {
1604                         if (suspend_layout)
1605                                 return;
1606
1607                         Draw (pevent.ClipRectangle, pevent.Graphics);
1608                 }
1609
1610                 internal void RepositionScrollBars ()
1611                 {
1612                         if (vscrollbar.is_visible) {
1613                                 vscrollbar.Size = new Size (vscrollbar.Width, items_area.Height);
1614                                 vscrollbar.Location = new Point (items_area.Width, 0);
1615                         }
1616
1617                         if (hscrollbar.is_visible) {
1618                                 hscrollbar.Size = new Size (items_area.Width, hscrollbar.Height);
1619                                 hscrollbar.Location = new Point (0, items_area.Height);
1620                         }
1621                 }
1622
1623                 // Add an item in the Selection array and marks it visually as selected
1624                 private void SelectItem (int index)
1625                 {
1626                         if (index == -1 || SelectedIndices.Contains (index))
1627                                 return;
1628
1629                         selection.Add (Items[index]);
1630                         InvalidateItem (index);
1631                 }               
1632                 
1633                 // An item navigation operation (mouse or keyboard) has caused to select a new item
1634                 internal void SelectedItemFromNavigation (int index)
1635                 {
1636                         switch (SelectionMode) {
1637                                 case SelectionMode.None: // Do nothing
1638                                         break;
1639                                 case SelectionMode.One: {
1640                                         SelectedIndex = index;
1641                                         break;
1642                                 }
1643                                 case SelectionMode.MultiSimple: {
1644                                         if (SelectedIndex == -1) {
1645                                                 SelectedIndex = index;
1646                                         } else {
1647
1648                                                 if (SelectedIndices.Contains (index))
1649                                                         UnSelectItem (index, true);
1650                                                 else {
1651                                                         SelectItem (index);
1652                                                         OnSelectedIndexChanged  (new EventArgs ());
1653                                                         OnSelectedValueChanged (new EventArgs ());
1654                                                 }
1655                                         }
1656                                         break;
1657                                 }
1658                                 
1659                                 case SelectionMode.MultiExtended: {
1660                                         if (SelectedIndex == -1) {
1661                                                 SelectedIndex = index;
1662                                         } else {
1663
1664                                                 if (ctrl_pressed == false && shift_pressed == false) {
1665                                                         ClearSelected ();
1666                                                 }
1667                                                 
1668                                                 if (shift_pressed == true) {
1669                                                         ShiftSelection (index);
1670                                                 } else { // ctrl_pressed or single item
1671                                                         SelectItem (index);
1672                                                 }
1673                                                 
1674                                                 OnSelectedIndexChanged  (new EventArgs ());
1675                                                 OnSelectedValueChanged (new EventArgs ());
1676                                         }
1677                                         break;
1678                                 }                               
1679                                 
1680                                 default:
1681                                         break;
1682                         }                       
1683                 }
1684                 
1685                 private void ShiftSelection (int index)
1686                 {
1687                         int shorter_item = -1, dist = Items.Count + 1, cur_dist;
1688                         
1689                         foreach (int idx in selected_indices) {
1690                                 if (idx > index) {
1691                                         cur_dist = idx - index;
1692                                 }
1693                                 else {
1694                                         cur_dist = index - idx;                                 
1695                                 }
1696                                                 
1697                                 if (cur_dist < dist) {
1698                                         dist = cur_dist;
1699                                         shorter_item = idx;
1700                                 }
1701                         }
1702                         
1703                         if (shorter_item != -1) {
1704                                 int start, end;
1705                                 
1706                                 if (shorter_item > index) {
1707                                         start = index;
1708                                         end = shorter_item;
1709                                 } else {
1710                                         start = shorter_item;
1711                                         end = index;
1712                                 }
1713                                 
1714                                 ClearSelected ();
1715                                 for (int idx = start; idx <= end; idx++) {
1716                                         SelectItem (idx);       
1717                                 }
1718                         }
1719                 }
1720                 
1721                 internal int FocusedItem {
1722                         get { return focused_item; }
1723                         set {                   
1724                                 if (focused_item == value)
1725                                         return;
1726
1727                                 int prev = focused_item;                        
1728                         
1729                                 focused_item = value;
1730                         
1731                                 if (has_focus == false)
1732                                         return;
1733
1734                                 if (prev != -1)
1735                                         InvalidateItem (prev);
1736                         
1737                                 if (value != -1)
1738                                         InvalidateItem (value);
1739                         }
1740                 }
1741
1742                 // Removes an item in the Selection array and marks it visually as unselected
1743                 private void UnSelectItem (int index, bool remove)
1744                 {
1745                         if (index == -1)
1746                                 return;
1747
1748                         if (remove)
1749                                 selection.Remove (Items[index]);
1750
1751                         InvalidateItem (index);
1752                 }
1753
1754                 StringFormat string_format;
1755                 internal StringFormat StringFormat {
1756                         get {
1757                                 if (string_format == null) {
1758                                         string_format = new StringFormat ();
1759                                         if (RightToLeft == RightToLeft.Yes)
1760                                                 string_format.Alignment = StringAlignment.Far;
1761                                         else
1762                                                 string_format.Alignment = StringAlignment.Near;
1763                                         if (use_tabstops)
1764                                                 string_format.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
1765                                 }
1766                                 return string_format;
1767                         }
1768
1769                 }
1770
1771                 internal virtual void CollectionChanged ()
1772                 {
1773                         if (sorted) 
1774                                 Sort ();
1775                         
1776                         if (Items.Count == 0) {
1777                                 selected_index = -1;
1778                                 focused_item = -1;
1779                                 top_index = 0;
1780                         }
1781                         
1782                         if (!IsHandleCreated || suspend_layout)
1783                                 return;
1784
1785                         Layout ();
1786
1787                         base.Refresh ();
1788                 }
1789
1790                 private void UpdateBounds ()
1791                 {
1792                         if (requested_height == -1)
1793                                 return;
1794
1795                         SetBounds(0, 0, 0, requested_height, BoundsSpecified.Height);
1796                 }
1797
1798                 private void UpdateScrollBars ()
1799                 {
1800                         items_area = ClientRectangle;
1801                         if (UpdateHorizontalScrollBar ()) {
1802                                 items_area.Height -= hscrollbar.Height;
1803                                 if (UpdateVerticalScrollBar ()) {
1804                                         items_area.Width -= vscrollbar.Width;
1805                                         UpdateHorizontalScrollBar ();
1806                                 }
1807                         } else if (UpdateVerticalScrollBar ()) {
1808                                 items_area.Width -= vscrollbar.Width;
1809                                 if (UpdateHorizontalScrollBar ()) {
1810                                         items_area.Height -= hscrollbar.Height;
1811                                         UpdateVerticalScrollBar ();
1812                                 }
1813                         }
1814
1815                         RepositionScrollBars ();
1816                 }
1817
1818                 /* Determines if the horizontal scrollbar has to be displyed */
1819                 private bool UpdateHorizontalScrollBar ()
1820                 {
1821                         bool show = false;
1822                         bool enabled = true;
1823
1824                         if (MultiColumn) {
1825                                 if (canvas_size.Width > items_area.Width) {
1826                                         show = true;
1827                                         hscrollbar.Maximum  = canvas_size.Width / ColumnWidthInternal - 1;
1828                                 } else if (ScrollAlwaysVisible == true) {
1829                                         enabled = false;
1830                                         show = true;
1831                                         hscrollbar.Maximum  = 0;
1832                                 }
1833                         } else if (canvas_size.Width > ClientRectangle.Width && HorizontalScrollbar) {
1834                                 show = true;                                    
1835                                 hscrollbar.Maximum = canvas_size.Width;
1836                                 hscrollbar.LargeChange = items_area.Width;
1837                         }
1838
1839                         hbar_offset = hscrollbar.Value;
1840                         hscrollbar.Enabled = enabled;
1841                         hscrollbar.Visible = show;
1842
1843                         return show;
1844                 }
1845
1846                 /* Determines if the vertical scrollbar has to be displyed */
1847                 private bool UpdateVerticalScrollBar ()
1848                 {
1849                         if (MultiColumn) {
1850                                 vscrollbar.Visible = false;
1851                                 return false;
1852                         }
1853
1854                         bool show = false;
1855                         bool enabled = true;
1856                         if (canvas_size.Height > items_area.Height) {
1857                                 show = true;
1858                                 vscrollbar.Maximum = Items.Count - 1;
1859                                 vscrollbar.LargeChange = items_area.Height / ItemHeight;
1860                         } else if (ScrollAlwaysVisible) {
1861                                 show = true;
1862                                 enabled = false;
1863                                 vscrollbar.Maximum = 0;
1864                         }
1865
1866                         vscrollbar.Enabled = enabled;
1867                         vscrollbar.Visible = show;
1868
1869                         return show;
1870                 }
1871
1872                 // Value Changed
1873                 private void VerticalScrollEvent (object sender, EventArgs e)
1874                 {
1875                         int top_item = top_index;
1876
1877                         top_index = /*row_count + */ vscrollbar.Value;
1878                         last_visible_index = LastVisibleItem ();
1879
1880                         int diff = top_item - top_index;
1881
1882                         XplatUI.ScrollWindow (Handle, items_area, 0, ItemHeight * diff, false);
1883                 }
1884
1885                 #endregion Private Methods
1886
1887                 [ListBindable (false)]
1888                 public class ObjectCollection : IList, ICollection, IEnumerable
1889                 {
1890                         internal class ListObjectComparer : IComparer
1891                         {
1892                                 private ListBox owner;
1893                         
1894                                 public ListObjectComparer (ListBox owner)
1895                                 {
1896                                         this.owner = owner;
1897                                 }
1898                                 
1899                                 public int Compare (object a, object b)
1900                                 {
1901                                         string str1 = a.ToString ();
1902                                         string str2 = b.ToString ();                                    
1903                                         return str1.CompareTo (str2);
1904                                 }
1905                         }
1906
1907                         private ListBox owner;
1908                         internal ArrayList object_items = new ArrayList ();
1909
1910                         public ObjectCollection (ListBox owner)
1911                         {
1912                                 this.owner = owner;
1913                         }
1914
1915                         public ObjectCollection (ListBox owner, object[] obj)
1916                         {
1917                                 this.owner = owner;
1918                                 AddRange (obj);
1919                         }
1920
1921                         public ObjectCollection (ListBox owner,  ObjectCollection obj)
1922                         {
1923                                 this.owner = owner;
1924                                 AddRange (obj);
1925                         }
1926
1927                         #region Public Properties
1928                         public int Count {
1929                                 get { return object_items.Count; }
1930                         }
1931
1932                         public bool IsReadOnly {
1933                                 get { return false; }
1934                         }
1935
1936                         [Browsable(false)]
1937                         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1938                         public virtual object this [int index] {
1939                                 get {
1940                                         if (index < 0 || index >= Count)
1941                                                 throw new ArgumentOutOfRangeException ("Index of out range");
1942
1943                                         return object_items[index];
1944                                 }
1945                                 set {
1946                                         if (index < 0 || index >= Count)
1947                                                 throw new ArgumentOutOfRangeException ("Index of out range");
1948                                         if (value == null)
1949                                                 throw new ArgumentNullException ("value");
1950
1951                                         object_items[index] = value;
1952                                         owner.CollectionChanged ();
1953                                 }
1954                         }
1955
1956                         bool ICollection.IsSynchronized {
1957                                 get { return false; }
1958                         }
1959
1960                         object ICollection.SyncRoot {
1961                                 get { return this; }
1962                         }
1963
1964                         bool IList.IsFixedSize {
1965                                 get { return false; }
1966                         }
1967
1968                         #endregion Public Properties
1969                         
1970                         #region Public Methods
1971                         public int Add (object item)
1972                         {
1973                                 int idx;
1974
1975                                 idx = AddItem (item);
1976                                 owner.CollectionChanged ();
1977                                 return idx;
1978                         }
1979
1980                         public void AddRange (object[] items)
1981                         {
1982                                 foreach (object mi in items)
1983                                         AddItem (mi);
1984
1985                                 owner.CollectionChanged ();
1986                         }
1987
1988                         public void AddRange (ObjectCollection col)
1989                         {
1990                                 foreach (object mi in col)
1991                                         AddItem (mi);
1992
1993                                 owner.CollectionChanged ();
1994                         }
1995
1996                         internal void AddRange (IList list)
1997                         {
1998                                 foreach (object mi in list)
1999                                         AddItem (mi);
2000
2001                                 owner.CollectionChanged ();
2002                         }
2003
2004                         public virtual void Clear ()
2005                         {
2006                                 owner.selection.Clear ();
2007                                 object_items.Clear ();
2008                                 owner.CollectionChanged ();
2009                         }
2010                         public bool Contains (object obj)
2011                         {
2012                                 return object_items.Contains (obj);
2013                         }
2014
2015                         public void CopyTo (object[] dest, int arrayIndex)
2016                         {
2017                                 object_items.CopyTo (dest, arrayIndex);
2018                         }
2019
2020                         void ICollection.CopyTo (Array dest, int index)
2021                         {
2022                                 object_items.CopyTo (dest, index);
2023                         }
2024
2025                         public IEnumerator GetEnumerator ()
2026                         {
2027                                 return object_items.GetEnumerator ();
2028                         }
2029
2030                         int IList.Add (object item)
2031                         {
2032                                 return Add (item);
2033                         }
2034
2035                         public int IndexOf (object value)
2036                         {
2037                                 return object_items.IndexOf (value);
2038                         }
2039
2040                         public void Insert (int index,  object item)
2041                         {
2042                                 if (index < 0 || index > Count)
2043                                         throw new ArgumentOutOfRangeException ("Index of out range");
2044                                         
2045                                 owner.BeginUpdate ();
2046                                 object_items.Insert (index, item);
2047                                 owner.CollectionChanged ();
2048                                 owner.EndUpdate ();
2049                         }
2050
2051                         public void Remove (object value)
2052                         {                               
2053                                 RemoveAt (IndexOf (value));                             
2054                         }
2055
2056                         public void RemoveAt (int index)
2057                         {
2058                                 if (index < 0 || index >= Count)
2059                                         throw new ArgumentOutOfRangeException ("Index of out range");
2060
2061                                 owner.selection.Remove (object_items [index]);
2062                                 object_items.RemoveAt (index);
2063                                 owner.CollectionChanged ();
2064                         }
2065                         #endregion Public Methods
2066
2067                         #region Private Methods
2068                         internal int AddItem (object item)
2069                         {
2070                                 if (item == null)
2071                                         throw new ArgumentNullException ("item");
2072
2073                                 int cnt = object_items.Count;
2074                                 object_items.Add (item);
2075                                 return cnt;
2076                         }
2077
2078                         internal void Sort ()
2079                         {
2080                                 object_items.Sort (new ListObjectComparer (owner));
2081                         }
2082
2083                         #endregion Private Methods
2084                 }
2085
2086                 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2087                 {
2088                         private ListBox owner;
2089
2090                         public SelectedIndexCollection (ListBox owner)
2091                         {
2092                                 this.owner = owner;
2093                         }
2094
2095                         #region Public Properties
2096                         [Browsable (false)]
2097                         public int Count {
2098                                 get { return owner.selection.Count; }
2099                         }
2100
2101                         public bool IsReadOnly {
2102                                 get { return true; }
2103                         }
2104
2105                         public int this [int index] {
2106                                 get {
2107                                         if (index < 0 || index >= Count)
2108                                                 throw new ArgumentOutOfRangeException ("Index of out range");
2109
2110                                         return owner.Items.IndexOf (owner.selection [index]);
2111                                 }
2112                         }
2113
2114                         bool ICollection.IsSynchronized {
2115                                 get { return true; }
2116                         }
2117
2118                         bool IList.IsFixedSize{
2119                                 get { return true; }
2120                         }
2121
2122                         object ICollection.SyncRoot {
2123                                 get { return this; }
2124                         }
2125
2126                         #endregion Public Properties
2127
2128                         #region Public Methods
2129                         public bool Contains (int selectedIndex)
2130                         {
2131                                 foreach (object o in owner.selection)
2132                                         if (owner.Items.IndexOf (o) == selectedIndex)
2133                                                 return true;
2134                                 return false;
2135                         }
2136
2137                         public void CopyTo (Array dest, int index)
2138                         {
2139                                 foreach (object o in owner.selection)
2140                                         dest.SetValue(owner.Items.IndexOf (o), index++);
2141                         }
2142
2143                         public IEnumerator GetEnumerator ()
2144                         {
2145                                 //FIXME: write an enumerator that uses owner.selection.GetEnumerator
2146                                 //  so that invalidation is write on selection changes
2147                                 ArrayList indices = new ArrayList ();
2148                                 foreach (object o in owner.selection)
2149                                         indices.Add (owner.Items.IndexOf (o));
2150                                 return indices.GetEnumerator ();
2151                         }
2152
2153                         int IList.Add (object obj)
2154                         {
2155                                 throw new NotSupportedException ();
2156                         }
2157
2158                         void IList.Clear ()
2159                         {
2160                                 throw new NotSupportedException ();
2161                         }
2162
2163                         bool IList.Contains (object selectedIndex)
2164                         {
2165                                 return Contains ((int)selectedIndex);
2166                         }
2167
2168                         int IList.IndexOf (object selectedIndex)
2169                         {
2170                                 return IndexOf ((int) selectedIndex);
2171                         }
2172
2173                         void IList.Insert (int index, object value)
2174                         {
2175                                 throw new NotSupportedException ();
2176                         }
2177
2178                         void IList.Remove (object value)
2179                         {
2180                                 throw new NotSupportedException ();
2181                         }
2182
2183                         void IList.RemoveAt (int index)
2184                         {
2185                                 throw new NotSupportedException ();
2186                         }
2187
2188                         object IList.this[int index]{
2189                                 get {return owner.Items.IndexOf (owner.selection [index]); }
2190                                 set {throw new NotImplementedException (); }
2191                         }
2192
2193                         public int IndexOf (int selectedIndex)
2194                         {
2195                                 for (int i = 0; i < owner.selection.Count; i++)
2196                                         if (owner.Items.IndexOf (owner.selection [i]) == selectedIndex)
2197                                                 return i;
2198                                 return -1;
2199                         }
2200                         #endregion Public Methods
2201                 }
2202
2203                 public class SelectedObjectCollection : IList, ICollection, IEnumerable
2204                 {
2205                         private ListBox owner;
2206
2207                         public SelectedObjectCollection (ListBox owner)
2208                         {
2209                                 this.owner = owner;
2210                         }
2211
2212                         #region Public Properties
2213                         public int Count {
2214                                 get { return owner.selection.Count; }
2215                         }
2216
2217                         public bool IsReadOnly {
2218                                 get { return true; }
2219                         }
2220
2221                         [Browsable(false)]
2222                         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2223                         public object this [int index] {
2224                                 get {
2225                                         if (index < 0 || index >= Count)
2226                                                 throw new ArgumentOutOfRangeException ("Index of out range");
2227
2228                                         return owner.selection [index];
2229                                 }
2230                                 set {throw new NotSupportedException ();}
2231                         }
2232
2233                         bool ICollection.IsSynchronized {
2234                                 get { return true; }
2235                         }
2236
2237                         object ICollection.SyncRoot {
2238                                 get { return this; }
2239                         }
2240
2241                         bool IList.IsFixedSize {
2242                                 get { return true; }
2243                         }
2244
2245                         #endregion Public Properties
2246
2247                         #region Public Methods
2248                         public bool Contains (object selectedObject)
2249                         {
2250                                 return owner.selection.Contains (selectedObject);
2251                         }
2252
2253                         public void CopyTo (Array dest, int index)
2254                         {
2255                                 owner.selection.CopyTo (dest, index);
2256                         }
2257
2258                         int IList.Add (object value)
2259                         {
2260                                 throw new NotSupportedException ();
2261                         }
2262
2263                         void IList.Clear ()
2264                         {
2265                                 throw new NotSupportedException ();
2266                         }
2267
2268                         void IList.Insert (int index, object value)
2269                         {
2270                                 throw new NotSupportedException ();
2271                         }
2272
2273                         void IList.Remove (object value)
2274                         {
2275                                 throw new NotSupportedException ();
2276                         }
2277
2278                         void IList.RemoveAt (int index)
2279                         {
2280                                 throw new NotSupportedException ();
2281                         }
2282         
2283                         public int IndexOf (object item)
2284                         {
2285                                 return owner.selection.IndexOf (item);
2286                         }
2287
2288                         public IEnumerator GetEnumerator ()
2289                         {
2290                                 return owner.selection.GetEnumerator ();
2291                         }
2292
2293                         #endregion Public Methods
2294                 }
2295
2296         }
2297 }
2298