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