aboutsummaryrefslogtreecommitdiff
path: root/source/ShiftUI/Widgets/ListBox.cs
diff options
context:
space:
mode:
authorMichaelTheShifter <[email protected]>2016-07-20 09:40:36 -0400
committerMichaelTheShifter <[email protected]>2016-07-20 09:40:36 -0400
commitd40fed5ce2bc806a91245adb18039634eac13ed0 (patch)
treef1d7168aee6db109ac2c738ad18c9db667a6ba69 /source/ShiftUI/Widgets/ListBox.cs
parentf1856e8ed30ed882229fd3fa2a4038122a5fb441 (diff)
downloadshiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.gz
shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.bz2
shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.zip
Move ShiftUI source code to ShiftOS
This'll be a lot easier to work on.
Diffstat (limited to 'source/ShiftUI/Widgets/ListBox.cs')
-rw-r--r--source/ShiftUI/Widgets/ListBox.cs3093
1 files changed, 3093 insertions, 0 deletions
diff --git a/source/ShiftUI/Widgets/ListBox.cs b/source/ShiftUI/Widgets/ListBox.cs
new file mode 100644
index 0000000..ed705f6
--- /dev/null
+++ b/source/ShiftUI/Widgets/ListBox.cs
@@ -0,0 +1,3093 @@
+/// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Copyright (c) 2004-2006 Novell, Inc.
+//
+// Authors:
+// Jordi Mas i Hernandez, [email protected]
+// Mike Kestner <[email protected]>
+//
+
+// COMPLETE
+
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.ComponentModel.Design.Serialization;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+
+namespace ShiftUI
+{
+ [DefaultProperty("Items")]
+ [DefaultEvent("SelectedIndexChanged")]
+ //[Designer ("ShiftUI.Design.ListBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+ [DefaultBindingProperty ("SelectedValue")]
+ [ClassInterface (ClassInterfaceType.AutoDispatch)]
+ [ComVisible (true)]
+ [ToolboxWidget]
+ public class ListBox : ListWidget
+ {
+ public const int DefaultItemHeight = 13;
+ public const int NoMatches = -1;
+
+ internal enum ItemNavigation
+ {
+ First,
+ Last,
+ Next,
+ Previous,
+ NextPage,
+ PreviousPage,
+ PreviousColumn,
+ NextColumn
+ }
+
+ Hashtable item_heights;
+ private int item_height = -1;
+ private int column_width = 0;
+ private int requested_height;
+ private DrawMode draw_mode = DrawMode.Normal;
+ private int horizontal_extent = 0;
+ private bool horizontal_scrollbar = false;
+ private bool integral_height = true;
+ private bool multicolumn = false;
+ private bool scroll_always_visible = false;
+ private SelectedIndexCollection selected_indices;
+ private SelectedObjectCollection selected_items;
+ private SelectionMode selection_mode = SelectionMode.One;
+ private bool sorted = false;
+ private bool use_tabstops = true;
+ private int column_width_internal = 120;
+ private ImplicitVScrollBar vscrollbar;
+ private ImplicitHScrollBar hscrollbar;
+ private int hbar_offset;
+ private bool suspend_layout;
+ private bool ctrl_pressed = false;
+ private bool shift_pressed = false;
+ private bool explicit_item_height = false;
+ private int top_index = 0;
+ private int last_visible_index = 0;
+ private Rectangle items_area;
+ private int focused_item = -1;
+ private ObjectCollection items;
+ private IntegerCollection custom_tab_offsets;
+ private Padding padding;
+ private bool use_custom_tab_offsets;
+
+ public ListBox ()
+ {
+ items = CreateItemCollection ();
+ selected_indices = new SelectedIndexCollection (this);
+ selected_items = new SelectedObjectCollection (this);
+
+ requested_height = bounds.Height;
+ InternalBorderStyle = BorderStyle.Fixed3D;
+ BackColor = ThemeEngine.Current.ColorControl;
+
+ /* Vertical scrollbar */
+ vscrollbar = new ImplicitVScrollBar ();
+ vscrollbar.Minimum = 0;
+ vscrollbar.SmallChange = 1;
+ vscrollbar.LargeChange = 1;
+ vscrollbar.Maximum = 0;
+ vscrollbar.ValueChanged += new EventHandler (VerticalScrollEvent);
+ vscrollbar.Visible = false;
+
+ /* Horizontal scrollbar */
+ hscrollbar = new ImplicitHScrollBar ();
+ hscrollbar.Minimum = 0;
+ hscrollbar.SmallChange = 1;
+ hscrollbar.LargeChange = 1;
+ hscrollbar.Maximum = 0;
+ hscrollbar.Visible = false;
+ hscrollbar.ValueChanged += new EventHandler (HorizontalScrollEvent);
+
+ Widgets.AddImplicit (vscrollbar);
+ Widgets.AddImplicit (hscrollbar);
+
+ /* Events */
+ MouseDown += new MouseEventHandler (OnMouseDownLB);
+ MouseMove += new MouseEventHandler (OnMouseMoveLB);
+ MouseUp += new MouseEventHandler (OnMouseUpLB);
+ MouseWheel += new MouseEventHandler (OnMouseWheelLB);
+ KeyUp += new KeyEventHandler (OnKeyUpLB);
+ GotFocus += new EventHandler (OnGotFocus);
+ LostFocus += new EventHandler (OnLostFocus);
+
+ SetStyle (Widgetstyles.UserPaint, false);
+
+ custom_tab_offsets = new IntegerCollection (this);
+ }
+
+ #region Events
+ static object DrawItemEvent = new object ();
+ static object MeasureItemEvent = new object ();
+ static object SelectedIndexChangedEvent = new object ();
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageChanged {
+ add { base.BackgroundImageChanged += value; }
+ remove { base.BackgroundImageChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler BackgroundImageLayoutChanged {
+ add { base.BackgroundImageLayoutChanged += value; }
+ remove { base.BackgroundImageLayoutChanged -= value; }
+ }
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event EventHandler Click {
+ add { base.Click += value; }
+ remove { base.Click -= value; }
+ }
+
+ public event DrawItemEventHandler DrawItem {
+ add { Events.AddHandler (DrawItemEvent, value); }
+ remove { Events.RemoveHandler (DrawItemEvent, value); }
+ }
+
+ public event MeasureItemEventHandler MeasureItem {
+ add { Events.AddHandler (MeasureItemEvent, value); }
+ remove { Events.RemoveHandler (MeasureItemEvent, value); }
+ }
+
+ [Browsable (true)]
+ //[EditorBrowsable (EditorBrowsableState.Always)]
+ public new event MouseEventHandler MouseClick {
+ add { base.MouseClick += value; }
+ remove { base.MouseClick -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event EventHandler PaddingChanged {
+ add { base.PaddingChanged += value; }
+ remove { base.PaddingChanged -= value; }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public new event PaintEventHandler Paint {
+ add { base.Paint += value; }
+ remove { base.Paint -= value; }
+ }
+
+ public event EventHandler SelectedIndexChanged {
+ add { Events.AddHandler (SelectedIndexChangedEvent, value); }
+ remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public new event EventHandler TextChanged {
+ add { base.TextChanged += value; }
+ remove { base.TextChanged -= value; }
+ }
+ #endregion // Events
+
+ #region UIA Framework Events
+ //NOTE:
+ // We are using Reflection to add/remove internal events.
+ // Class ListProvider uses the events.
+ //
+ //Event used to generate UIA Selection Pattern
+ static object UIASelectionModeChangedEvent = new object ();
+
+ internal event EventHandler UIASelectionModeChanged {
+ add { Events.AddHandler (UIASelectionModeChangedEvent, value); }
+ remove { Events.RemoveHandler (UIASelectionModeChangedEvent, value); }
+ }
+
+ internal void OnUIASelectionModeChangedEvent ()
+ {
+ EventHandler eh = (EventHandler) Events [UIASelectionModeChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ static object UIAFocusedItemChangedEvent = new object ();
+
+ internal event EventHandler UIAFocusedItemChanged {
+ add { Events.AddHandler (UIAFocusedItemChangedEvent, value); }
+ remove { Events.RemoveHandler (UIAFocusedItemChangedEvent, value); }
+ }
+
+ internal void OnUIAFocusedItemChangedEvent ()
+ {
+ EventHandler eh = (EventHandler) Events [UIAFocusedItemChangedEvent];
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+ #endregion UIA Framework Events
+
+ #region Public Properties
+ public override Color BackColor {
+ get { return base.BackColor; }
+ set {
+ if (base.BackColor == value)
+ return;
+
+ base.BackColor = value;
+ base.Refresh (); // Careful. Calling the base method is not the same that calling
+ } // the overriden one that refresh also all the items
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override Image BackgroundImage {
+ get { return base.BackgroundImage; }
+ set {
+ base.BackgroundImage = value;
+ base.Refresh ();
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ public override ImageLayout BackgroundImageLayout {
+ get { return base.BackgroundImageLayout; }
+ set { base.BackgroundImageLayout = value; }
+ }
+
+ [DefaultValue (BorderStyle.Fixed3D)]
+ [DispId(-504)]
+ public BorderStyle BorderStyle {
+ get { return InternalBorderStyle; }
+ set {
+ InternalBorderStyle = value;
+ UpdateListBoxBounds ();
+ }
+ }
+
+ [DefaultValue (0)]
+ [Localizable (true)]
+ public int ColumnWidth {
+ get { return column_width; }
+ set {
+ if (value < 0)
+ throw new ArgumentException ("A value less than zero is assigned to the property.");
+
+ column_width = value;
+
+ if (value == 0)
+ ColumnWidthInternal = 120;
+ else
+ ColumnWidthInternal = value;
+
+ base.Refresh ();
+ }
+ }
+
+ protected override CreateParams CreateParams {
+ get { return base.CreateParams;}
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ public IntegerCollection CustomTabOffsets {
+ get { return custom_tab_offsets; }
+ }
+
+ protected override Size DefaultSize {
+ get { return new Size (120, 96); }
+ }
+
+ [RefreshProperties(RefreshProperties.Repaint)]
+ [DefaultValue (DrawMode.Normal)]
+ public virtual DrawMode DrawMode {
+ get { return draw_mode; }
+ set {
+ if (!Enum.IsDefined (typeof (DrawMode), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for DrawMode", value));
+
+ if (value == DrawMode.OwnerDrawVariable && multicolumn == true)
+ throw new ArgumentException ("Cannot have variable height and multicolumn");
+
+ if (draw_mode == value)
+ return;
+
+ draw_mode = value;
+
+ if (draw_mode == DrawMode.OwnerDrawVariable)
+ item_heights = new Hashtable ();
+ else
+ item_heights = null;
+
+ if (Parent != null)
+ Parent.PerformLayout (this, "DrawMode");
+ base.Refresh ();
+ }
+ }
+
+ public override Font Font {
+ get { return base.Font; }
+ set { base.Font = value; }
+ }
+
+ public override Color ForeColor {
+ get { return base.ForeColor; }
+ set {
+ if (base.ForeColor == value)
+ return;
+
+ base.ForeColor = value;
+ base.Refresh ();
+ }
+ }
+
+ [DefaultValue (0)]
+ [Localizable (true)]
+ public int HorizontalExtent {
+ get { return horizontal_extent; }
+ set {
+ if (horizontal_extent == value)
+ return;
+
+ horizontal_extent = value;
+ base.Refresh ();
+ }
+ }
+
+ [DefaultValue (false)]
+ [Localizable (true)]
+ public bool HorizontalScrollbar {
+ get { return horizontal_scrollbar; }
+ set {
+ if (horizontal_scrollbar == value)
+ return;
+
+ horizontal_scrollbar = value;
+ UpdateScrollBars ();
+ base.Refresh ();
+ }
+ }
+
+ [DefaultValue (true)]
+ [Localizable (true)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ public bool IntegralHeight {
+ get { return integral_height; }
+ set {
+ if (integral_height == value)
+ return;
+
+ integral_height = value;
+ UpdateListBoxBounds ();
+ }
+ }
+
+ [DefaultValue (13)]
+ [Localizable (true)]
+ [RefreshProperties(RefreshProperties.Repaint)]
+ public virtual int ItemHeight {
+ get {
+ if (item_height == -1) {
+ SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
+ item_height = (int) sz.Height;
+ }
+ return item_height;
+ }
+ set {
+ if (value > 255)
+ throw new ArgumentOutOfRangeException ("The ItemHeight property was set beyond 255 pixels");
+
+ explicit_item_height = true;
+ if (item_height == value)
+ return;
+
+ item_height = value;
+ if (IntegralHeight)
+ UpdateListBoxBounds ();
+ LayoutListBox ();
+ }
+ }
+
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+ [Localizable (true)]
+ //[Editor ("ShiftUI.Design.ListWidgetstringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
+ [MergableProperty (false)]
+ public ObjectCollection Items {
+ get { return items; }
+ }
+
+ [DefaultValue (false)]
+ public bool MultiColumn {
+ get { return multicolumn; }
+ set {
+ if (multicolumn == value)
+ return;
+
+ if (value == true && DrawMode == DrawMode.OwnerDrawVariable)
+ throw new ArgumentException ("A multicolumn ListBox cannot have a variable-sized height.");
+
+ multicolumn = value;
+ LayoutListBox ();
+ Invalidate ();
+ }
+ }
+
+ [Browsable (false)]
+ //[EditorBrowsable (EditorBrowsableState.Never)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public new Padding Padding {
+ get { return padding; }
+ set { padding = value; }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public int PreferredHeight {
+ get {
+ int itemsHeight = 0;
+ if (draw_mode == DrawMode.Normal)
+ itemsHeight = FontHeight * items.Count;
+ else if (draw_mode == DrawMode.OwnerDrawFixed)
+ itemsHeight = ItemHeight * items.Count;
+ else if (draw_mode == DrawMode.OwnerDrawVariable) {
+ for (int i = 0; i < items.Count; i++)
+ itemsHeight += (int) item_heights [Items [i]];
+ }
+
+ return itemsHeight;
+ }
+ }
+
+ public override RightToLeft RightToLeft {
+ get { return base.RightToLeft; }
+ set {
+ base.RightToLeft = value;
+ if (base.RightToLeft == RightToLeft.Yes)
+ StringFormat.Alignment = StringAlignment.Far;
+ else
+ StringFormat.Alignment = StringAlignment.Near;
+ base.Refresh ();
+ }
+ }
+
+ // Only affects the Vertical ScrollBar
+ [DefaultValue (false)]
+ [Localizable (true)]
+ public bool ScrollAlwaysVisible {
+ get { return scroll_always_visible; }
+ set {
+ if (scroll_always_visible == value)
+ return;
+
+ scroll_always_visible = value;
+ UpdateScrollBars ();
+ }
+ }
+
+ [Bindable(true)]
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public override int SelectedIndex {
+ get {
+ if (selected_indices == null)
+ return -1;
+
+ return selected_indices.Count > 0 ? selected_indices [0] : -1;
+ }
+ set {
+ if (value < -1 || value >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ if (SelectionMode == SelectionMode.None)
+ throw new ArgumentException ("cannot call this method if SelectionMode is SelectionMode.None");
+
+ if (value == -1)
+ selected_indices.Clear ();
+ else
+ selected_indices.Add (value);
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public SelectedIndexCollection SelectedIndices {
+ get { return selected_indices; }
+ }
+
+ [Bindable(true)]
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public object SelectedItem {
+ get {
+ if (SelectedItems.Count > 0)
+ return SelectedItems[0];
+ else
+ return null;
+ }
+ set {
+ if (value != null && !Items.Contains (value))
+ return; // FIXME: this is probably an exception
+
+ SelectedIndex = value == null ? - 1 : Items.IndexOf (value);
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public SelectedObjectCollection SelectedItems {
+ get {return selected_items;}
+ }
+
+ [DefaultValue (SelectionMode.One)]
+ public virtual SelectionMode SelectionMode {
+ get { return selection_mode; }
+ set {
+ if (!Enum.IsDefined (typeof (SelectionMode), value))
+ throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for SelectionMode", value));
+
+ if (selection_mode == value)
+ return;
+
+ selection_mode = value;
+
+ switch (selection_mode) {
+ case SelectionMode.None:
+ SelectedIndices.Clear ();
+ break;
+
+ case SelectionMode.One:
+ // FIXME: Probably this can be improved
+ ArrayList old_selection = (ArrayList) SelectedIndices.List.Clone ();
+ for (int i = 1; i < old_selection.Count; i++)
+ SelectedIndices.Remove ((int)old_selection [i]);
+ break;
+
+ default:
+ break;
+ }
+
+ // UIA Framework: Generates SelectionModeChanged event.
+ OnUIASelectionModeChangedEvent ();
+ }
+ }
+
+ [DefaultValue (false)]
+ public bool Sorted {
+ get { return sorted; }
+ set {
+ if (sorted == value)
+ return;
+
+ sorted = value;
+ if (sorted)
+ Sort ();
+ }
+ }
+
+ [Bindable (false)]
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ public override string Text {
+ get {
+ if (SelectionMode != SelectionMode.None && SelectedIndex != -1)
+ return GetItemText (SelectedItem);
+
+ return base.Text;
+ }
+ set {
+
+ base.Text = value;
+
+ if (SelectionMode == SelectionMode.None)
+ return;
+
+ int index;
+
+ index = FindStringExact (value);
+
+ if (index == -1)
+ return;
+
+ SelectedIndex = index;
+ }
+ }
+
+ [Browsable (false)]
+ //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public int TopIndex {
+ get { return top_index; }
+ set {
+ if (value == top_index)
+ return;
+
+ if (value < 0 || value >= Items.Count)
+ return;
+
+ int page_size = (items_area.Height / ItemHeight);
+
+ if (Items.Count < page_size)
+ value = 0;
+ else if (!multicolumn)
+ top_index = Math.Min (value, Items.Count - page_size);
+ else
+ top_index = value;
+
+ UpdateTopItem ();
+ base.Refresh ();
+ }
+ }
+
+ [Browsable (false)]
+ [DefaultValue (false)]
+ public bool UseCustomTabOffsets {
+ get { return use_custom_tab_offsets; }
+ set {
+ if (use_custom_tab_offsets != value) {
+ use_custom_tab_offsets = value;
+ CalculateTabStops ();
+ }
+ }
+ }
+
+ [DefaultValue (true)]
+ public bool UseTabStops {
+ get { return use_tabstops; }
+ set {
+ if (use_tabstops == value)
+ return;
+
+ use_tabstops = value;
+ CalculateTabStops ();
+ }
+ }
+
+ protected override bool AllowSelection {
+ get {
+ return SelectionMode != SelectionMode.None;
+ }
+ }
+ #endregion Public Properties
+
+ #region Private Properties
+
+ private int ColumnWidthInternal {
+ get { return column_width_internal; }
+ set { column_width_internal = value; }
+ }
+
+ private int row_count = 1;
+ private int RowCount {
+ get {
+ return MultiColumn ? row_count : Items.Count;
+ }
+ }
+
+ #endregion Private Properties
+
+ #region UIA Framework Properties
+
+ internal ScrollBar UIAHScrollBar {
+ get { return hscrollbar; }
+ }
+
+ internal ScrollBar UIAVScrollBar {
+ get { return vscrollbar; }
+ }
+
+ #endregion UIA Framework Properties
+
+ #region Public Methods
+ [Obsolete ("this method has been deprecated")]
+ protected virtual void AddItemsCore (object[] value)
+ {
+ Items.AddRange (value);
+ }
+
+ public void BeginUpdate ()
+ {
+ suspend_layout = true;
+ }
+
+ public void ClearSelected ()
+ {
+ selected_indices.Clear ();
+ }
+
+ protected virtual ObjectCollection CreateItemCollection ()
+ {
+ return new ObjectCollection (this);
+ }
+
+ public void EndUpdate ()
+ {
+ suspend_layout = false;
+ LayoutListBox ();
+ base.Refresh ();
+ }
+
+ public int FindString (String s)
+ {
+ return FindString (s, -1);
+ }
+
+ public int FindString (string s, int startIndex)
+ {
+ if (Items.Count == 0)
+ return -1; // No exception throwing if empty
+
+ if (startIndex < -1 || startIndex >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ startIndex = (startIndex == Items.Count - 1) ? 0 : startIndex + 1;
+
+ int i = startIndex;
+ while (true) {
+ string text = GetItemText (Items [i]);
+ if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (text, s,
+ CompareOptions.IgnoreCase))
+ return i;
+
+ i = (i == Items.Count - 1) ? 0 : i + 1;
+ if (i == startIndex)
+ break;
+ }
+
+ return NoMatches;
+ }
+
+ public int FindStringExact (string s)
+ {
+ return FindStringExact (s, -1);
+ }
+
+ public int FindStringExact (string s, int startIndex)
+ {
+ if (Items.Count == 0)
+ return -1; // No exception throwing if empty
+
+ if (startIndex < -1 || startIndex >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ startIndex = (startIndex + 1 == Items.Count) ? 0 : startIndex + 1;
+
+ int i = startIndex;
+ while (true) {
+ if (String.Compare (GetItemText (Items[i]), s, true) == 0)
+ return i;
+
+ i = (i + 1 == Items.Count) ? 0 : i + 1;
+ if (i == startIndex)
+ break;
+ }
+
+ return NoMatches;
+ }
+
+ public int GetItemHeight (int index)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ if (DrawMode == DrawMode.OwnerDrawVariable && IsHandleCreated == true) {
+
+ object o = Items [index];
+ if (item_heights.Contains (o))
+ return (int) item_heights [o];
+
+ MeasureItemEventArgs args = new MeasureItemEventArgs (DeviceContext, index, ItemHeight);
+ OnMeasureItem (args);
+ item_heights [o] = args.ItemHeight;
+ return args.ItemHeight;
+ }
+
+ return ItemHeight;
+ }
+
+ public Rectangle GetItemRectangle (int index)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("GetItemRectangle index out of range.");
+
+ Rectangle rect = new Rectangle ();
+
+ if (MultiColumn) {
+ int col = index / RowCount;
+ int y = index;
+ if (y < 0) // We convert it to valid positive value
+ y += RowCount * (top_index / RowCount);
+ rect.Y = (y % RowCount) * ItemHeight;
+ rect.X = (col - (top_index / RowCount)) * ColumnWidthInternal;
+ rect.Height = ItemHeight;
+ rect.Width = ColumnWidthInternal;
+ } else {
+ rect.X = 0;
+ rect.Height = GetItemHeight (index);
+ rect.Width = items_area.Width;
+
+ if (DrawMode == DrawMode.OwnerDrawVariable) {
+ rect.Y = 0;
+ if (index >= top_index) {
+ for (int i = top_index; i < index; i++) {
+ rect.Y += GetItemHeight (i);
+ }
+ } else {
+ for (int i = index; i < top_index; i++) {
+ rect.Y -= GetItemHeight (i);
+ }
+ }
+ } else {
+ rect.Y = ItemHeight * (index - top_index);
+ }
+ }
+
+ if (this is CheckedListBox)
+ rect.Width += 15;
+
+ return rect;
+ }
+
+ //[EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
+ {
+ bounds.Height = requested_height;
+
+ return base.GetScaledBounds (bounds, factor, specified);
+ }
+
+ public bool GetSelected (int index)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ return SelectedIndices.Contains (index);
+ }
+
+ public int IndexFromPoint (Point p)
+ {
+ return IndexFromPoint (p.X, p.Y);
+ }
+
+ // Only returns visible points
+ public int IndexFromPoint (int x, int y)
+ {
+
+ if (Items.Count == 0) {
+ return -1;
+ }
+
+ for (int i = top_index; i <= last_visible_index; i++) {
+ if (GetItemRectangle (i).Contains (x,y) == true)
+ return i;
+ }
+
+ return -1;
+ }
+
+ protected override void OnChangeUICues (UICuesEventArgs e)
+ {
+ base.OnChangeUICues (e);
+ }
+
+ protected override void OnDataSourceChanged (EventArgs e)
+ {
+ base.OnDataSourceChanged (e);
+ BindDataItems ();
+
+ if (DataSource == null || DataManager == null) {
+ SelectedIndex = -1;
+ } else {
+ SelectedIndex = DataManager.Position;
+ }
+ }
+
+ protected override void OnDisplayMemberChanged (EventArgs e)
+ {
+ base.OnDisplayMemberChanged (e);
+
+ if (DataManager == null || !IsHandleCreated)
+ return;
+
+ BindDataItems ();
+ base.Refresh ();
+ }
+
+ protected virtual void OnDrawItem (DrawItemEventArgs e)
+ {
+ switch (DrawMode) {
+ case DrawMode.OwnerDrawFixed:
+ case DrawMode.OwnerDrawVariable:
+ DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
+ if (eh != null)
+ eh (this, e);
+
+ break;
+
+ default:
+ ThemeEngine.Current.DrawListBoxItem (this, e);
+ break;
+ }
+ }
+
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+
+ if (use_tabstops)
+ StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
+
+ if (explicit_item_height) {
+ base.Refresh ();
+ } else {
+ SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
+ item_height = (int) sz.Height;
+ if (IntegralHeight)
+ UpdateListBoxBounds ();
+ LayoutListBox ();
+ }
+ }
+
+ protected override void OnHandleCreated (EventArgs e)
+ {
+ base.OnHandleCreated (e);
+
+ if (IntegralHeight)
+ UpdateListBoxBounds ();
+
+ LayoutListBox ();
+ EnsureVisible (focused_item);
+ }
+
+ protected override void OnHandleDestroyed (EventArgs e)
+ {
+ base.OnHandleDestroyed (e);
+ }
+
+ protected virtual void OnMeasureItem (MeasureItemEventArgs e)
+ {
+ if (draw_mode != DrawMode.OwnerDrawVariable)
+ return;
+
+ MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnParentChanged (EventArgs e)
+ {
+ base.OnParentChanged (e);
+ }
+
+ protected override void OnResize (EventArgs e)
+ {
+ base.OnResize (e);
+ if (canvas_size.IsEmpty || MultiColumn)
+ LayoutListBox ();
+
+ Invalidate ();
+ }
+
+ protected override void OnSelectedIndexChanged (EventArgs e)
+ {
+ base.OnSelectedIndexChanged (e);
+
+ EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ protected override void OnSelectedValueChanged (EventArgs e)
+ {
+ base.OnSelectedValueChanged (e);
+ }
+
+ public override void Refresh ()
+ {
+ if (draw_mode == DrawMode.OwnerDrawVariable)
+ item_heights.Clear ();
+
+ base.Refresh ();
+ }
+
+ protected override void RefreshItem (int index)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ if (draw_mode == DrawMode.OwnerDrawVariable)
+ item_heights.Remove (Items [index]);
+ }
+
+ protected override void RefreshItems ()
+ {
+ for (int i = 0; i < Items.Count; i++) {
+ RefreshItem (i);
+ }
+
+ LayoutListBox ();
+ Refresh ();
+ }
+
+ public override void ResetBackColor ()
+ {
+ base.ResetBackColor ();
+ }
+
+ public override void ResetForeColor ()
+ {
+ base.ResetForeColor ();
+ }
+
+ protected override void ScaleWidget (SizeF factor, BoundsSpecified specified)
+ {
+ base.ScaleWidget (factor, specified);
+ }
+
+ private int SnapHeightToIntegral (int height)
+ {
+ int border;
+
+ switch (border_style) {
+ case BorderStyle.Fixed3D:
+ border = ThemeEngine.Current.Border3DSize.Height;
+ break;
+ case BorderStyle.FixedSingle:
+ border = ThemeEngine.Current.BorderSize.Height;
+ break;
+ case BorderStyle.None:
+ default:
+ border = 0;
+ break;
+ }
+
+ height -= (2 * border);
+ height -= height % ItemHeight;
+ height += (2 * border);
+
+ return height;
+ }
+
+ protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
+ {
+ if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height)
+ requested_height = height;
+
+ if (IntegralHeight && IsHandleCreated)
+ height = SnapHeightToIntegral (height);
+
+ base.SetBoundsCore (x, y, width, height, specified);
+ UpdateScrollBars ();
+ last_visible_index = LastVisibleItem ();
+ }
+
+ protected override void SetItemCore (int index, object value)
+ {
+ if (index < 0 || index >= Items.Count)
+ return;
+
+ Items[index] = value;
+ }
+
+ protected override void SetItemsCore (IList value)
+ {
+ BeginUpdate ();
+ try {
+ Items.Clear ();
+ Items.AddItems (value);
+ } finally {
+ EndUpdate ();
+ }
+ }
+
+ public void SetSelected (int index, bool value)
+ {
+ if (index < 0 || index >= Items.Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ if (SelectionMode == SelectionMode.None)
+ throw new InvalidOperationException ();
+
+ if (value)
+ SelectedIndices.Add (index);
+ else
+ SelectedIndices.Remove (index);
+ }
+
+ protected virtual void Sort ()
+ {
+ Sort (true);
+ }
+
+ //
+ // Sometimes we could need to Sort, and request a Refresh
+ // in a different place, to not have the painting done twice
+ //
+ void Sort (bool paint)
+ {
+ if (Items.Count == 0)
+ return;
+
+ Items.Sort ();
+
+ if (paint)
+ base.Refresh ();
+ }
+
+ public override string ToString ()
+ {
+ return base.ToString ();
+ }
+
+ protected virtual void WmReflectCommand (ref Message m)
+ {
+ }
+
+ protected override void WndProc (ref Message m)
+ {
+ if ((Msg)m.Msg == Msg.WM_KEYDOWN) {
+ if (ProcessKeyMessage (ref m))
+ m.Result = IntPtr.Zero;
+ else {
+ HandleKeyDown ((Keys)m.WParam.ToInt32 ());
+ DefWndProc (ref m);
+ }
+
+ return;
+ }
+
+ base.WndProc (ref m);
+ }
+
+ #endregion Public Methods
+
+ #region Private Methods
+
+ private void CalculateTabStops ()
+ {
+ if (use_tabstops) {
+ if (use_custom_tab_offsets) {
+ float[] f = new float[custom_tab_offsets.Count];
+ custom_tab_offsets.CopyTo (f, 0);
+ StringFormat.SetTabStops (0, f);
+ }
+ else
+ StringFormat.SetTabStops (0, new float[] { (float)(Font.Height * 3.7) });
+ } else
+ StringFormat.SetTabStops (0, new float[0]);
+
+ this.Invalidate ();
+ }
+
+ private Size canvas_size;
+
+ private void LayoutListBox ()
+ {
+ if (!IsHandleCreated || suspend_layout)
+ return;
+
+ if (MultiColumn)
+ LayoutMultiColumn ();
+ else
+ LayoutSingleColumn ();
+
+ last_visible_index = LastVisibleItem ();
+ UpdateScrollBars ();
+ }
+
+ private void LayoutSingleColumn ()
+ {
+ int height, width;
+
+ switch (DrawMode) {
+ case DrawMode.OwnerDrawVariable:
+ height = 0;
+ width = HorizontalExtent;
+ for (int i = 0; i < Items.Count; i++) {
+ height += GetItemHeight (i);
+ }
+ break;
+
+ case DrawMode.OwnerDrawFixed:
+ height = Items.Count * ItemHeight;
+ width = HorizontalExtent;
+ break;
+
+ case DrawMode.Normal:
+ default:
+ height = Items.Count * ItemHeight;
+ width = 0;
+ for (int i = 0; i < Items.Count; i++) {
+ SizeF sz = TextRenderer.MeasureString (GetItemText (Items[i]), Font);
+ int t = (int)sz.Width;
+
+ if (this is CheckedListBox)
+ t += 15;
+
+ if (t > width)
+ width = t;
+ }
+ break;
+ }
+
+ canvas_size = new Size (width, height);
+ }
+
+ private void LayoutMultiColumn ()
+ {
+ int usable_height = ClientRectangle.Height - (ScrollAlwaysVisible ? hscrollbar.Height : 0);
+ row_count = Math.Max (1, usable_height / ItemHeight);
+
+ int cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
+ Size sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
+ if (!ScrollAlwaysVisible && sz.Width > ClientRectangle.Width && row_count > 1) {
+ usable_height = ClientRectangle.Height - hscrollbar.Height;
+ row_count = Math.Max (1, usable_height / ItemHeight);
+ cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
+ sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
+ }
+ canvas_size = sz;
+ }
+
+ internal void Draw (Rectangle clip, Graphics dc)
+ {
+ Theme theme = ThemeEngine.Current;
+
+ if (hscrollbar.Visible && vscrollbar.Visible) {
+ // Paint the dead space in the bottom right corner
+ Rectangle rect = new Rectangle (hscrollbar.Right, vscrollbar.Bottom, vscrollbar.Width, hscrollbar.Height);
+ if (rect.IntersectsWith (clip))
+ dc.FillRectangle (theme.ResPool.GetSolidBrush (theme.ColorControl), rect);
+ }
+
+ dc.FillRectangle (theme.ResPool.GetSolidBrush (BackColor), items_area);
+
+ if (Items.Count == 0)
+ return;
+
+ for (int i = top_index; i <= last_visible_index; i++) {
+ Rectangle rect = GetItemDisplayRectangle (i, top_index);
+
+ if (!clip.IntersectsWith (rect))
+ continue;
+
+ DrawItemState state = DrawItemState.None;
+
+ if (SelectedIndices.Contains (i))
+ state |= DrawItemState.Selected;
+
+ if (has_focus && FocusedItem == i)
+ state |= DrawItemState.Focus;
+
+ if (MultiColumn == false && hscrollbar != null && hscrollbar.Visible) {
+ rect.X -= hscrollbar.Value;
+ rect.Width += hscrollbar.Value;
+ }
+
+ Color fore_color = !Enabled ? ThemeEngine.Current.ColorGrayText :
+ (state & DrawItemState.Selected) != 0 ? ThemeEngine.Current.ColorHighlightText : ForeColor;
+ OnDrawItem (new DrawItemEventArgs (dc, Font, rect, i, state, fore_color, BackColor));
+ }
+ }
+
+ // Converts a GetItemRectangle to a one that we can display
+ internal Rectangle GetItemDisplayRectangle (int index, int first_displayble)
+ {
+ Rectangle item_rect;
+ Rectangle first_item_rect = GetItemRectangle (first_displayble);
+ item_rect = GetItemRectangle (index);
+ item_rect.X -= first_item_rect.X;
+ item_rect.Y -= first_item_rect.Y;
+
+ // Subtract the checkboxes from the width
+ if (this is CheckedListBox)
+ item_rect.Width -= 14;
+
+ return item_rect;
+ }
+
+ // Value Changed
+ private void HorizontalScrollEvent (object sender, EventArgs e)
+ {
+ if (multicolumn) {
+ int top_item = top_index;
+ int last_item = last_visible_index;
+
+ top_index = RowCount * hscrollbar.Value;
+ last_visible_index = LastVisibleItem ();
+
+ if (top_item != top_index || last_item != last_visible_index)
+ Invalidate (items_area);
+ }
+ else {
+ int old_offset = hbar_offset;
+ hbar_offset = hscrollbar.Value;
+
+ if (hbar_offset < 0)
+ hbar_offset = 0;
+
+ if (IsHandleCreated) {
+ XplatUI.ScrollWindow (Handle, items_area, old_offset - hbar_offset, 0, false);
+
+ // Invalidate the previous selection border, to keep it properly updated.
+ Rectangle selection_border_area = new Rectangle (items_area.Width - (hbar_offset - old_offset) - 3, 0,
+ 3, items_area.Height);
+ Invalidate (selection_border_area);
+ }
+ }
+ }
+
+ // Only returns visible points. The diference of with IndexFromPoint is that the rectangle
+ // has screen coordinates
+ private int IndexAtClientPoint (int x, int y)
+ {
+ if (Items.Count == 0)
+ return -1;
+
+ if (x < 0)
+ x = 0;
+ else if (x > ClientRectangle.Right)
+ x = ClientRectangle.Right;
+
+ if (y < 0)
+ y = 0;
+ else if (y > ClientRectangle.Bottom)
+ y = ClientRectangle.Bottom;
+
+ for (int i = top_index; i <= last_visible_index; i++)
+ if (GetItemDisplayRectangle (i, top_index).Contains (x, y))
+ return i;
+
+ return -1;
+ }
+
+ internal override bool IsInputCharInternal (char charCode)
+ {
+ return true;
+ }
+
+ private int LastVisibleItem ()
+ {
+ Rectangle item_rect;
+ int top_y = items_area.Y + items_area.Height;
+ int i = 0;
+
+ if (top_index >= Items.Count)
+ return top_index;
+
+ for (i = top_index; i < Items.Count; i++) {
+ item_rect = GetItemDisplayRectangle (i, top_index);
+ if (MultiColumn) {
+ if (item_rect.X > items_area.Width)
+ return i - 1;
+ } else {
+ if (item_rect.Y + item_rect.Height > top_y)
+ return i;
+ }
+ }
+ return i - 1;
+ }
+
+ private void UpdateTopItem ()
+ {
+ if (MultiColumn) {
+ int col = top_index / RowCount;
+
+ if (col > hscrollbar.Maximum)
+ hscrollbar.Value = hscrollbar.Maximum;
+ else
+ hscrollbar.Value = col;
+ } else {
+ if (top_index > vscrollbar.Maximum)
+ vscrollbar.Value = vscrollbar.Maximum;
+ else
+ vscrollbar.Value = top_index;
+ Scroll (vscrollbar, vscrollbar.Value - top_index);
+ }
+ }
+
+ // Navigates to the indicated item and returns the new item
+ private int NavigateItemVisually (ItemNavigation navigation)
+ {
+ int page_size, columns, selected_index = -1;
+
+ if (multicolumn) {
+ columns = items_area.Width / ColumnWidthInternal;
+ page_size = columns * RowCount;
+ if (page_size == 0) {
+ page_size = RowCount;
+ }
+ } else {
+ page_size = items_area.Height / ItemHeight;
+ }
+
+ switch (navigation) {
+
+ case ItemNavigation.PreviousColumn: {
+ if (SelectedIndex - RowCount < 0) {
+ return -1;
+ }
+
+ if (SelectedIndex - RowCount < top_index) {
+ top_index = SelectedIndex - RowCount;
+ UpdateTopItem ();
+ }
+
+ selected_index = SelectedIndex - RowCount;
+ break;
+ }
+
+ case ItemNavigation.NextColumn: {
+ if (SelectedIndex + RowCount >= Items.Count) {
+ break;
+ }
+
+ if (SelectedIndex + RowCount > last_visible_index) {
+ top_index = SelectedIndex;
+ UpdateTopItem ();
+ }
+
+ selected_index = SelectedIndex + RowCount;
+ break;
+ }
+
+ case ItemNavigation.First: {
+ top_index = 0;
+ selected_index = 0;
+ UpdateTopItem ();
+ break;
+ }
+
+ case ItemNavigation.Last: {
+
+ int rows = items_area.Height / ItemHeight;
+
+ if (multicolumn) {
+ selected_index = Items.Count - 1;
+ break;
+ }
+ if (Items.Count < rows) {
+ top_index = 0;
+ selected_index = Items.Count - 1;
+ UpdateTopItem ();
+ } else {
+ top_index = Items.Count - rows;
+ selected_index = Items.Count - 1;
+ UpdateTopItem ();
+ }
+ break;
+ }
+
+ case ItemNavigation.Next: {
+ if (FocusedItem == Items.Count - 1)
+ return -1;
+
+ if (multicolumn) {
+ selected_index = FocusedItem + 1;
+ break;
+ }
+
+ int bottom = 0;
+ ArrayList heights = new ArrayList ();
+ if (draw_mode == DrawMode.OwnerDrawVariable) {
+ for (int i = top_index; i <= FocusedItem + 1; i++) {
+ int h = GetItemHeight (i);
+ bottom += h;
+ heights.Add (h);
+ }
+ } else {
+ bottom = ((FocusedItem + 1) - top_index + 1) * ItemHeight;
+ }
+
+ if (bottom >= items_area.Height) {
+ int overhang = bottom - items_area.Height;
+
+ int offset = 0;
+ if (draw_mode == DrawMode.OwnerDrawVariable)
+ while (overhang > 0)
+ overhang -= (int) heights [offset];
+ else
+ offset = (int) Math.Ceiling ((float)overhang / (float) ItemHeight);
+ top_index += offset;
+ UpdateTopItem ();
+ }
+ selected_index = FocusedItem + 1;
+ break;
+ }
+
+ case ItemNavigation.Previous: {
+ if (FocusedItem > 0) {
+ if (FocusedItem - 1 < top_index) {
+ top_index--;
+ UpdateTopItem ();
+ }
+ selected_index = FocusedItem - 1;
+ }
+ break;
+ }
+
+ case ItemNavigation.NextPage: {
+ if (Items.Count < page_size) {
+ NavigateItemVisually (ItemNavigation.Last);
+ break;
+ }
+
+ if (FocusedItem + page_size - 1 >= Items.Count) {
+ top_index = Items.Count - page_size;
+ UpdateTopItem ();
+ selected_index = Items.Count - 1;
+ }
+ else {
+ if (FocusedItem + page_size - 1 > last_visible_index) {
+ top_index = FocusedItem;
+ UpdateTopItem ();
+ }
+
+ selected_index = FocusedItem + page_size - 1;
+ }
+
+ break;
+ }
+
+ case ItemNavigation.PreviousPage: {
+
+ int rows = items_area.Height / ItemHeight;
+ if (FocusedItem - (rows - 1) <= 0) {
+ top_index = 0;
+ UpdateTopItem ();
+ selected_index = 0;
+ }
+ else {
+ if (SelectedIndex - (rows - 1) < top_index) {
+ top_index = FocusedItem - (rows - 1);
+ UpdateTopItem ();
+ }
+
+ selected_index = FocusedItem - (rows - 1);
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ return selected_index;
+ }
+
+
+ private void OnGotFocus (object sender, EventArgs e)
+ {
+ if (Items.Count == 0)
+ return;
+
+ if (FocusedItem == -1)
+ FocusedItem = 0;
+
+ InvalidateItem (FocusedItem);
+ }
+
+ private void OnLostFocus (object sender, EventArgs e)
+ {
+ if (FocusedItem != -1)
+ InvalidateItem (FocusedItem);
+ }
+
+ private bool KeySearch (Keys key)
+ {
+ char c = (char) key;
+ if (!Char.IsLetterOrDigit (c))
+ return false;
+
+ int idx = FindString (c.ToString (), SelectedIndex);
+ if (idx != ListBox.NoMatches)
+ SelectedIndex = idx;
+
+ return true;
+ }
+
+ internal void HandleKeyDown (Keys key)
+ {
+ int new_item = -1;
+
+ if (Items.Count == 0)
+ return;
+
+ if (KeySearch (key))
+ return;
+
+ switch (key) {
+
+ case Keys.ControlKey:
+ ctrl_pressed = true;
+ break;
+
+ case Keys.ShiftKey:
+ shift_pressed = true;
+ break;
+
+ case Keys.Home:
+ new_item = NavigateItemVisually (ItemNavigation.First);
+ break;
+
+ case Keys.End:
+ new_item = NavigateItemVisually (ItemNavigation.Last);
+ break;
+
+ case Keys.Up:
+ new_item = NavigateItemVisually (ItemNavigation.Previous);
+ break;
+
+ case Keys.Down:
+ new_item = NavigateItemVisually (ItemNavigation.Next);
+ break;
+
+ case Keys.PageUp:
+ new_item = NavigateItemVisually (ItemNavigation.PreviousPage);
+ break;
+
+ case Keys.PageDown:
+ new_item = NavigateItemVisually (ItemNavigation.NextPage);
+ break;
+
+ case Keys.Right:
+ if (multicolumn == true) {
+ new_item = NavigateItemVisually (ItemNavigation.NextColumn);
+ }
+ break;
+
+ case Keys.Left:
+ if (multicolumn == true) {
+ new_item = NavigateItemVisually (ItemNavigation.PreviousColumn);
+ }
+ break;
+
+ case Keys.Space:
+ if (selection_mode == SelectionMode.MultiSimple) {
+ SelectedItemFromNavigation (FocusedItem);
+ }
+ break;
+
+
+ default:
+ break;
+ }
+
+ if (new_item != -1) {
+ FocusedItem = new_item;
+
+ if (selection_mode != SelectionMode.MultiSimple)
+ SelectedItemFromNavigation (new_item);
+ }
+ }
+
+ private void OnKeyUpLB (object sender, KeyEventArgs e)
+ {
+ switch (e.KeyCode) {
+ case Keys.ControlKey:
+ ctrl_pressed = false;
+ break;
+ case Keys.ShiftKey:
+ shift_pressed = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ internal void InvalidateItem (int index)
+ {
+ if (!IsHandleCreated)
+ return;
+ Rectangle bounds = GetItemDisplayRectangle (index, top_index);
+ if (ClientRectangle.IntersectsWith (bounds))
+ Invalidate (bounds);
+ }
+
+ internal virtual void OnItemClick (int index)
+ {
+ OnSelectedIndexChanged (EventArgs.Empty);
+ OnSelectedValueChanged (EventArgs.Empty);
+ }
+
+ int anchor = -1;
+ int[] prev_selection;
+ bool button_pressed = false;
+ Point button_pressed_loc = new Point (-1, -1);
+
+ private void SelectExtended (int index)
+ {
+ SuspendLayout ();
+
+ ArrayList new_selection = new ArrayList ();
+ int start = anchor < index ? anchor : index;
+ int end = anchor > index ? anchor : index;
+ for (int i = start; i <= end; i++)
+ new_selection.Add (i);
+
+ if (ctrl_pressed)
+ foreach (int i in prev_selection)
+ if (!new_selection.Contains (i))
+ new_selection.Add (i);
+
+ // Need to make a copy since we can't enumerate and modify the collection
+ // at the same time
+ ArrayList sel_indices = (ArrayList) selected_indices.List.Clone ();
+ foreach (int i in sel_indices)
+ if (!new_selection.Contains (i))
+ selected_indices.Remove (i);
+
+ foreach (int i in new_selection)
+ if (!sel_indices.Contains (i))
+ selected_indices.AddCore (i);
+ ResumeLayout ();
+ }
+
+ private void OnMouseDownLB (object sender, MouseEventArgs e)
+ {
+ // Only do stuff with the left mouse button
+ if ((e.Button & MouseButtons.Left) == 0)
+ return;
+
+ int index = IndexAtClientPoint (e.X, e.Y);
+ if (index == -1)
+ return;
+
+ switch (SelectionMode) {
+ case SelectionMode.One:
+ SelectedIndices.AddCore (index); // Unselects previous one
+ break;
+
+ case SelectionMode.MultiSimple:
+ if (SelectedIndices.Contains (index))
+ SelectedIndices.RemoveCore (index);
+ else
+ SelectedIndices.AddCore (index);
+ break;
+
+ case SelectionMode.MultiExtended:
+ shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
+ ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Widget) != 0;
+
+ if (shift_pressed) {
+ SelectedIndices.ClearCore ();
+ SelectExtended (index);
+ break;
+ }
+
+ anchor = index;
+
+ if (ctrl_pressed) {
+ prev_selection = new int [SelectedIndices.Count];
+ SelectedIndices.CopyTo (prev_selection, 0);
+
+ if (SelectedIndices.Contains (index))
+ SelectedIndices.RemoveCore (index);
+ else
+ SelectedIndices.AddCore (index);
+
+ break;
+ }
+
+ SelectedIndices.ClearCore ();
+ SelectedIndices.AddCore (index);
+ break;
+
+ case SelectionMode.None:
+ break;
+ default:
+ return;
+ }
+
+ button_pressed = true;
+ button_pressed_loc = new Point (e.X, e.Y);
+ FocusedItem = index;
+ }
+
+ private void OnMouseMoveLB (object sender, MouseEventArgs e)
+ {
+ // Don't take into account MouseMove events generated with MouseDown
+ if (!button_pressed || button_pressed_loc == new Point (e.X, e.Y))
+ return;
+
+ int index = IndexAtClientPoint (e.X, e.Y);
+ if (index == -1)
+ return;
+
+ switch (SelectionMode) {
+ case SelectionMode.One:
+ SelectedIndices.AddCore (index); // Unselects previous one
+ break;
+
+ case SelectionMode.MultiSimple:
+ break;
+
+ case SelectionMode.MultiExtended:
+ SelectExtended (index);
+ break;
+
+ case SelectionMode.None:
+ break;
+ default:
+ return;
+ }
+
+ FocusedItem = index;
+ }
+
+ internal override void OnDragDropEnd (DragDropEffects effects)
+ {
+ // In the case of a DnD operation (started on MouseDown)
+ // there will be no MouseUp event, so we need to reset
+ // the state here
+ button_pressed = false;
+ }
+
+ private void OnMouseUpLB (object sender, MouseEventArgs e)
+ {
+ // Only do stuff with the left mouse button
+ if ((e.Button & MouseButtons.Left) == 0)
+ return;
+
+ if (e.Clicks > 1) {
+ OnDoubleClick (EventArgs.Empty);
+ OnMouseDoubleClick (e);
+ }
+ else if (e.Clicks == 1) {
+ OnClick (EventArgs.Empty);
+ OnMouseClick (e);
+ }
+
+ if (!button_pressed)
+ return;
+
+ int index = IndexAtClientPoint (e.X, e.Y);
+ OnItemClick (index);
+ button_pressed = ctrl_pressed = shift_pressed = false;
+ }
+
+ private void Scroll (ScrollBar scrollbar, int delta)
+ {
+ if (delta == 0 || !scrollbar.Visible || !scrollbar.Enabled)
+ return;
+
+ int max;
+ if (scrollbar == hscrollbar)
+ max = hscrollbar.Maximum - (items_area.Width / ColumnWidthInternal) + 1;
+ else
+ max = vscrollbar.Maximum - (items_area.Height / ItemHeight) + 1;
+
+ int val = scrollbar.Value + delta;
+ if (val > max)
+ val = max;
+ else if (val < scrollbar.Minimum)
+ val = scrollbar.Minimum;
+ scrollbar.Value = val;
+ }
+
+ private void OnMouseWheelLB (object sender, MouseEventArgs me)
+ {
+ if (Items.Count == 0)
+ return;
+
+ int lines = me.Delta / 120;
+
+ if (MultiColumn)
+ Scroll (hscrollbar, -SystemInformation.MouseWheelScrollLines * lines);
+ else
+ Scroll (vscrollbar, -lines);
+ }
+
+ internal override void OnPaintInternal (PaintEventArgs pevent)
+ {
+ if (suspend_layout)
+ return;
+
+ Draw (pevent.ClipRectangle, pevent.Graphics);
+ }
+
+ internal void RepositionScrollBars ()
+ {
+ if (vscrollbar.is_visible) {
+ vscrollbar.Size = new Size (vscrollbar.Width, items_area.Height);
+ vscrollbar.Location = new Point (items_area.Width, 0);
+ }
+
+ if (hscrollbar.is_visible) {
+ hscrollbar.Size = new Size (items_area.Width, hscrollbar.Height);
+ hscrollbar.Location = new Point (0, items_area.Height);
+ }
+ }
+
+ // An item navigation operation (mouse or keyboard) has caused to select a new item
+ internal void SelectedItemFromNavigation (int index)
+ {
+ switch (SelectionMode) {
+ case SelectionMode.None:
+ // .Net doesn't select the item, only ensures that it's visible
+ // and fires the selection related events
+ EnsureVisible (index);
+ OnSelectedIndexChanged (EventArgs.Empty);
+ OnSelectedValueChanged (EventArgs.Empty);
+ break;
+ case SelectionMode.One: {
+ SelectedIndex = index;
+ break;
+ }
+ case SelectionMode.MultiSimple: {
+ if (SelectedIndex == -1) {
+ SelectedIndex = index;
+ } else {
+
+ if (SelectedIndices.Contains (index))
+ SelectedIndices.Remove (index);
+ else {
+ SelectedIndices.AddCore (index);
+
+ OnSelectedIndexChanged (EventArgs.Empty);
+ OnSelectedValueChanged (EventArgs.Empty);
+ }
+ }
+ break;
+ }
+
+ case SelectionMode.MultiExtended: {
+ if (SelectedIndex == -1) {
+ SelectedIndex = index;
+ } else {
+
+ if (ctrl_pressed == false && shift_pressed == false) {
+ SelectedIndices.Clear ();
+ }
+
+ if (shift_pressed == true) {
+ ShiftSelection (index);
+ } else { // ctrl_pressed or single item
+ SelectedIndices.AddCore (index);
+
+ }
+
+ OnSelectedIndexChanged (EventArgs.Empty);
+ OnSelectedValueChanged (EventArgs.Empty);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ private void ShiftSelection (int index)
+ {
+ int shorter_item = -1, dist = Items.Count + 1, cur_dist;
+
+ foreach (int idx in selected_indices) {
+ if (idx > index) {
+ cur_dist = idx - index;
+ } else {
+ cur_dist = index - idx;
+ }
+
+ if (cur_dist < dist) {
+ dist = cur_dist;
+ shorter_item = idx;
+ }
+ }
+
+ if (shorter_item != -1) {
+ int start, end;
+
+ if (shorter_item > index) {
+ start = index;
+ end = shorter_item;
+ } else {
+ start = shorter_item;
+ end = index;
+ }
+
+ selected_indices.Clear ();
+ for (int idx = start; idx <= end; idx++) {
+ selected_indices.AddCore (idx);
+ }
+ }
+ }
+
+ internal int FocusedItem {
+ get { return focused_item; }
+ set {
+ if (focused_item == value)
+ return;
+
+ int prev = focused_item;
+
+ focused_item = value;
+
+ if (has_focus == false)
+ return;
+
+ if (prev != -1)
+ InvalidateItem (prev);
+
+ if (value != -1)
+ InvalidateItem (value);
+
+ // UIA Framework: Generates FocusedItemChanged event.
+ OnUIAFocusedItemChangedEvent ();
+ }
+ }
+
+ StringFormat string_format;
+ internal StringFormat StringFormat {
+ get {
+ if (string_format == null) {
+ string_format = new StringFormat ();
+ string_format.FormatFlags = StringFormatFlags.NoWrap;
+
+ if (RightToLeft == RightToLeft.Yes)
+ string_format.Alignment = StringAlignment.Far;
+ else
+ string_format.Alignment = StringAlignment.Near;
+ CalculateTabStops ();
+ }
+ return string_format;
+ }
+ }
+
+ internal virtual void CollectionChanged ()
+ {
+ if (sorted)
+ Sort (false);
+
+ if (Items.Count == 0) {
+ selected_indices.List.Clear ();
+ focused_item = -1;
+ top_index = 0;
+ }
+ if (Items.Count <= focused_item)
+ focused_item = Items.Count - 1;
+
+ if (!IsHandleCreated || suspend_layout)
+ return;
+
+ LayoutListBox ();
+
+ base.Refresh ();
+ }
+
+ void EnsureVisible (int index)
+ {
+ if (!IsHandleCreated || index == -1)
+ return;
+
+ if (index < top_index) {
+ top_index = index;
+ UpdateTopItem ();
+ Invalidate ();
+ } else if (!multicolumn) {
+ int rows = items_area.Height / ItemHeight;
+ if (index >= (top_index + rows))
+ top_index = index - rows + 1;
+
+ UpdateTopItem ();
+ } else {
+ int rows = Math.Max (1, items_area.Height / ItemHeight);
+ int cols = Math.Max (1, items_area.Width / ColumnWidthInternal);
+
+ if (index >= (top_index + (rows * cols))) {
+ int incolumn = index / rows;
+ top_index = (incolumn - (cols - 1)) * rows;
+
+ UpdateTopItem ();
+ Invalidate ();
+ }
+ }
+ }
+
+ private void UpdateListBoxBounds ()
+ {
+ if (IsHandleCreated)
+ SetBoundsInternal (bounds.X, bounds.Y, bounds.Width, IntegralHeight ? SnapHeightToIntegral (requested_height) : requested_height, BoundsSpecified.None);
+ }
+
+ private void UpdateScrollBars ()
+ {
+ items_area = ClientRectangle;
+ if (UpdateHorizontalScrollBar ()) {
+ items_area.Height -= hscrollbar.Height;
+ if (UpdateVerticalScrollBar ()) {
+ items_area.Width -= vscrollbar.Width;
+ UpdateHorizontalScrollBar ();
+ }
+ } else if (UpdateVerticalScrollBar ()) {
+ items_area.Width -= vscrollbar.Width;
+ if (UpdateHorizontalScrollBar ()) {
+ items_area.Height -= hscrollbar.Height;
+ UpdateVerticalScrollBar ();
+ }
+ }
+
+ RepositionScrollBars ();
+ }
+
+ /* Determines if the horizontal scrollbar has to be displyed */
+ private bool UpdateHorizontalScrollBar ()
+ {
+ bool show = false;
+ bool enabled = true;
+
+ if (MultiColumn) {
+ if (canvas_size.Width > items_area.Width) {
+ show = true;
+ hscrollbar.Maximum = canvas_size.Width / ColumnWidthInternal - 1;
+ } else if (ScrollAlwaysVisible == true) {
+ enabled = false;
+ show = true;
+ hscrollbar.Maximum = 0;
+ }
+ } else if (canvas_size.Width > ClientRectangle.Width && HorizontalScrollbar) {
+ show = true;
+ hscrollbar.Maximum = canvas_size.Width;
+ hscrollbar.LargeChange = Math.Max (0, items_area.Width);
+ } else if (scroll_always_visible && horizontal_scrollbar) {
+ show = true;
+ enabled = false;
+ hscrollbar.Maximum = 0;
+ }
+
+ hbar_offset = hscrollbar.Value;
+ hscrollbar.Enabled = enabled;
+ hscrollbar.Visible = show;
+
+ return show;
+ }
+
+ /* Determines if the vertical scrollbar has to be displyed */
+ private bool UpdateVerticalScrollBar ()
+ {
+ if (MultiColumn || (Items.Count == 0 && !scroll_always_visible)) {
+ vscrollbar.Visible = false;
+ return false;
+ } else if (Items.Count == 0) {
+ vscrollbar.Visible = true;
+ vscrollbar.Enabled = false;
+ vscrollbar.Maximum = 0;
+ return true;
+ }
+
+ bool show = false;
+ bool enabled = true;
+ if (canvas_size.Height > items_area.Height) {
+ show = true;
+ vscrollbar.Maximum = Items.Count - 1;
+ vscrollbar.LargeChange = Math.Max (items_area.Height / ItemHeight, 0);
+ } else if (ScrollAlwaysVisible) {
+ show = true;
+ enabled = false;
+ vscrollbar.Maximum = 0;
+ }
+
+ vscrollbar.Enabled = enabled;
+ vscrollbar.Visible = show;
+
+ return show;
+ }
+
+ // Value Changed
+ private void VerticalScrollEvent (object sender, EventArgs e)
+ {
+ int top_item = top_index;
+
+ top_index = /*row_count + */ vscrollbar.Value;
+ last_visible_index = LastVisibleItem ();
+
+ int delta = (top_item - top_index) * ItemHeight;
+ if (DrawMode == DrawMode.OwnerDrawVariable) {
+ delta = 0;
+
+ if (top_index < top_item)
+ for (int i = top_index; i < top_item; i++)
+ delta += GetItemHeight (i);
+ else
+ for (int i = top_item; i < top_index; i++)
+ delta -= GetItemHeight (i);
+ }
+
+ if (IsHandleCreated)
+ XplatUI.ScrollWindow (Handle, items_area, 0, delta, false);
+ }
+
+ #endregion Private Methods
+
+ public class IntegerCollection : IList, ICollection, IEnumerable
+ {
+ private ListBox owner;
+ private List<int> list;
+
+ #region Public Constructor
+ public IntegerCollection (ListBox owner)
+ {
+ this.owner = owner;
+ list = new List<int> ();
+ }
+ #endregion
+
+ #region Public Properties
+ [Browsable (false)]
+ public int Count {
+ get { return list.Count; }
+ }
+
+ public int this [int index] {
+ get { return list[index]; }
+ set { list[index] = value; owner.CalculateTabStops (); }
+ }
+ #endregion
+
+ #region Public Methods
+ public int Add (int item)
+ {
+ // This collection does not allow duplicates
+ if (!list.Contains (item)) {
+ list.Add (item);
+ list.Sort ();
+ owner.CalculateTabStops ();
+ }
+
+ return list.IndexOf (item);
+ }
+
+ public void AddRange (int[] items)
+ {
+ AddItems (items);
+ }
+
+ public void AddRange (IntegerCollection value)
+ {
+ AddItems (value);
+ }
+
+ void AddItems (IList items)
+ {
+ if (items == null)
+ throw new ArgumentNullException ("items");
+
+ foreach (int i in items)
+ if (!list.Contains (i))
+ list.Add (i);
+
+ list.Sort ();
+ }
+
+ public void Clear ()
+ {
+ list.Clear ();
+ owner.CalculateTabStops ();
+ }
+
+ public bool Contains (int item)
+ {
+ return list.Contains (item);
+ }
+
+ public void CopyTo (Array destination, int index)
+ {
+ for (int i = 0; i < list.Count; i++)
+ destination.SetValue (list[i], index++);
+ }
+
+ public int IndexOf (int item)
+ {
+ return list.IndexOf (item);
+ }
+
+ public void Remove (int item)
+ {
+ list.Remove (item);
+ list.Sort ();
+ owner.CalculateTabStops ();
+ }
+
+ public void RemoveAt (int index)
+ {
+ if (index < 0)
+ throw new IndexOutOfRangeException ();
+
+ list.RemoveAt (index);
+ list.Sort ();
+ owner.CalculateTabStops ();
+ }
+ #endregion
+
+ #region IEnumerable Members
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return list.GetEnumerator ();
+ }
+ #endregion
+
+ #region IList Members
+ int IList.Add (object item)
+ {
+ int? intValue = item as int?;
+ if (!intValue.HasValue)
+ throw new ArgumentException ("item");
+ return Add (intValue.Value);
+ }
+
+ void IList.Clear ()
+ {
+ Clear ();
+ }
+
+ bool IList.Contains (object item)
+ {
+ int? intValue = item as int?;
+ if (!intValue.HasValue)
+ return false;
+ return Contains (intValue.Value);
+ }
+
+ int IList.IndexOf (object item)
+ {
+ int? intValue = item as int?;
+ if (!intValue.HasValue)
+ return -1;
+ return IndexOf (intValue.Value);
+ }
+
+ void IList.Insert (int index, object value)
+ {
+ throw new NotSupportedException (string.Format (
+ CultureInfo.InvariantCulture, "No items "
+ + "can be inserted into {0}, since it is"
+ + " a sorted collection.", this.GetType ()));
+ }
+
+ bool IList.IsFixedSize
+ {
+ get { return false; }
+ }
+
+ bool IList.IsReadOnly
+ {
+ get { return false; }
+ }
+
+ void IList.Remove (object value)
+ {
+ int? intValue = value as int?;
+ if (!intValue.HasValue)
+ throw new ArgumentException ("value");
+
+ Remove (intValue.Value);
+ }
+
+ void IList.RemoveAt (int index)
+ {
+ RemoveAt (index);
+ }
+
+ object IList.this[int index] {
+ get { return this[index]; }
+ set { this[index] = (int)value; }
+ }
+ #endregion
+
+ #region ICollection Members
+ bool ICollection.IsSynchronized {
+ get { return true; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return this; }
+ }
+ #endregion
+ }
+
+ [ListBindable (false)]
+ public class ObjectCollection : IList, ICollection, IEnumerable
+ {
+ internal class ListObjectComparer : IComparer
+ {
+ public int Compare (object a, object b)
+ {
+ string str1 = a.ToString ();
+ string str2 = b.ToString ();
+ return str1.CompareTo (str2);
+ }
+ }
+
+ private ListBox owner;
+ internal ArrayList object_items = new ArrayList ();
+
+ #region UIA Framework Events
+ //NOTE:
+ // We are using Reflection to add/remove internal events.
+ // Class ListProvider uses the events.
+ //
+ //Event used to generate UIA StructureChangedEvent
+ static object UIACollectionChangedEvent = new object ();
+
+ internal event CollectionChangeEventHandler UIACollectionChanged {
+ add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
+ remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
+ }
+
+ internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
+ {
+ CollectionChangeEventHandler eh
+ = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
+ if (eh != null)
+ eh (owner, args);
+ }
+ #endregion UIA Framework Events
+
+ public ObjectCollection (ListBox owner)
+ {
+ this.owner = owner;
+ }
+
+ public ObjectCollection (ListBox owner, object[] value)
+ {
+ this.owner = owner;
+ AddRange (value);
+ }
+
+ public ObjectCollection (ListBox owner, ObjectCollection value)
+ {
+ this.owner = owner;
+ AddRange (value);
+ }
+
+ #region Public Properties
+ public int Count {
+ get { return object_items.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return false; }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public virtual object this [int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ return object_items[index];
+ }
+ set {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ //UIA Framework event: Item Removed
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, object_items [index]));
+
+ object_items[index] = value;
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, value));
+
+ owner.CollectionChanged ();
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return this; }
+ }
+
+ bool IList.IsFixedSize {
+ get { return false; }
+ }
+
+ #endregion Public Properties
+
+ #region Public Methods
+ public int Add (object item)
+ {
+ int idx;
+ object[] selectedItems = null;
+
+ // we need to remember the original selected items so that we can update the indices
+ if (owner.sorted) {
+ selectedItems = new object[owner.SelectedItems.Count];
+ owner.SelectedItems.CopyTo (selectedItems, 0);
+ }
+
+ idx = AddItem (item);
+ owner.CollectionChanged ();
+
+ // If we are sorted, the item probably moved indexes, get the real one
+ if (owner.sorted) {
+ // update indices of selected items
+ owner.SelectedIndices.Clear ();
+ for (int i = 0; i < selectedItems.Length; i++) {
+ owner.SelectedIndex = this.IndexOf (selectedItems [i]);
+ }
+ return this.IndexOf (item);
+ }
+
+ return idx;
+ }
+
+ public void AddRange (object[] items)
+ {
+ AddItems (items);
+ }
+
+ public void AddRange (ObjectCollection value)
+ {
+ AddItems (value);
+ }
+
+ internal void AddItems (IList items)
+ {
+ if (items == null)
+ throw new ArgumentNullException ("items");
+
+ foreach (object mi in items)
+ AddItem (mi);
+
+ owner.CollectionChanged ();
+ }
+
+ public virtual void Clear ()
+ {
+ owner.selected_indices.ClearCore ();
+ object_items.Clear ();
+ owner.CollectionChanged ();
+
+ //UIA Framework event: Items list cleared
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null));
+ }
+
+ public bool Contains (object value)
+ {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ return object_items.Contains (value);
+ }
+
+ public void CopyTo (object[] destination, int arrayIndex)
+ {
+ object_items.CopyTo (destination, arrayIndex);
+ }
+
+ void ICollection.CopyTo (Array destination, int index)
+ {
+ object_items.CopyTo (destination, index);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ return object_items.GetEnumerator ();
+ }
+
+ int IList.Add (object item)
+ {
+ return Add (item);
+ }
+
+ public int IndexOf (object value)
+ {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ return object_items.IndexOf (value);
+ }
+
+ public void Insert (int index, object item)
+ {
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+ if (item == null)
+ throw new ArgumentNullException ("item");
+
+ owner.BeginUpdate ();
+ object_items.Insert (index, item);
+ owner.CollectionChanged ();
+ owner.EndUpdate ();
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
+ }
+
+ public void Remove (object value)
+ {
+ if (value == null)
+ return;
+
+ int index = IndexOf (value);
+ if (index != -1)
+ RemoveAt (index);
+ }
+
+ public void RemoveAt (int index)
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ //UIA Framework element removed
+ object removed = object_items [index];
+ UpdateSelection (index);
+ object_items.RemoveAt (index);
+ owner.CollectionChanged ();
+
+ //UIA Framework event: Item Removed
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, removed));
+ }
+ #endregion Public Methods
+
+ #region Private Methods
+ internal int AddItem (object item)
+ {
+ if (item == null)
+ throw new ArgumentNullException ("item");
+
+ int cnt = object_items.Count;
+ object_items.Add (item);
+
+ //UIA Framework event: Item Added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
+
+ return cnt;
+ }
+
+ // we receive the index to be removed
+ void UpdateSelection (int removed_index)
+ {
+ owner.selected_indices.Remove (removed_index);
+
+ if (owner.selection_mode != SelectionMode.None) {
+ int last_idx = object_items.Count - 1;
+
+ // if the last item was selected, remove it from selection,
+ // since it will become invalid after the removal
+ if (owner.selected_indices.Contains (last_idx)) {
+ owner.selected_indices.Remove (last_idx);
+
+ // in SelectionMode.One try to put the selection on the new last item
+ int new_idx = last_idx - 1;
+ if (owner.selection_mode == SelectionMode.One && new_idx > -1)
+ owner.selected_indices.Add (new_idx);
+ }
+ }
+
+ }
+
+ internal void Sort ()
+ {
+ object_items.Sort (new ListObjectComparer ());
+ }
+
+ #endregion Private Methods
+ }
+
+ public class SelectedIndexCollection : IList, ICollection, IEnumerable
+ {
+ private ListBox owner;
+ ArrayList selection;
+ bool sorting_needed; // Selection state retrieval is done sorted - we do it lazyly
+
+ #region UIA Framework Events
+
+ //NOTE:
+ // We are using Reflection to add/remove internal events.
+ // Class ListProvider uses the events.
+ //
+ //Event used to generate UIA StructureChangedEvent
+ static object UIACollectionChangedEvent = new object ();
+
+ internal event CollectionChangeEventHandler UIACollectionChanged {
+ add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
+ remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
+ }
+
+ internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
+ {
+ CollectionChangeEventHandler eh
+ = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
+ if (eh != null)
+ eh (owner, args);
+ }
+
+ #endregion UIA Framework Events
+
+
+ public SelectedIndexCollection (ListBox owner)
+ {
+ this.owner = owner;
+ selection = new ArrayList ();
+ }
+
+ #region Public Properties
+ [Browsable (false)]
+ public int Count {
+ get { return selection.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return true; }
+ }
+
+ public int this [int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ CheckSorted ();
+ return (int)selection [index];
+ }
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return true; }
+ }
+
+ bool IList.IsFixedSize{
+ get { return true; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return selection; }
+ }
+
+ #endregion Public Properties
+
+ #region Public Methods
+ public void Add (int index)
+ {
+ if (AddCore (index)) {
+ owner.OnSelectedIndexChanged (EventArgs.Empty);
+ owner.OnSelectedValueChanged (EventArgs.Empty);
+ }
+ }
+
+ // Need to separate selection logic from events,
+ // since selection changes using keys/mouse handle them their own way
+ internal bool AddCore (int index)
+ {
+ if (selection.Contains (index))
+ return false;
+
+ if (index == -1) // Weird MS behaviour
+ return false;
+ if (index < -1 || index >= owner.Items.Count)
+ throw new ArgumentOutOfRangeException ("index");
+ if (owner.selection_mode == SelectionMode.None)
+ throw new InvalidOperationException ("Cannot call this method when selection mode is SelectionMode.None");
+
+ if (owner.selection_mode == SelectionMode.One && Count > 0) // Unselect previously selected item
+ RemoveCore ((int)selection [0]);
+
+ selection.Add (index);
+ sorting_needed = true;
+ owner.EnsureVisible (index);
+ owner.FocusedItem = index;
+ owner.InvalidateItem (index);
+
+ // UIA Framework event: Selected item added
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, index));
+
+ return true;
+ }
+
+ public void Clear ()
+ {
+ if (ClearCore ()) {
+ owner.OnSelectedIndexChanged (EventArgs.Empty);
+ owner.OnSelectedValueChanged (EventArgs.Empty);
+ }
+ }
+
+ internal bool ClearCore ()
+ {
+ if (selection.Count == 0)
+ return false;
+
+ foreach (int index in selection)
+ owner.InvalidateItem (index);
+
+ selection.Clear ();
+
+ // UIA Framework event: Selected items list updated
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, -1));
+
+ return true;
+ }
+
+ public bool Contains (int selectedIndex)
+ {
+ foreach (int index in selection)
+ if (index == selectedIndex)
+ return true;
+ return false;
+ }
+
+ public void CopyTo (Array destination, int index)
+ {
+ CheckSorted ();
+ selection.CopyTo (destination, index);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ CheckSorted ();
+ return selection.GetEnumerator ();
+ }
+
+ // FIXME: Probably we can avoid sorting when calling
+ // IndexOf (imagine a scenario where multiple removal of items
+ // happens)
+ public void Remove (int index)
+ {
+ // Separate logic from events here too
+ if (RemoveCore (index)) {
+ owner.OnSelectedIndexChanged (EventArgs.Empty);
+ owner.OnSelectedValueChanged (EventArgs.Empty);
+ }
+ }
+
+ internal bool RemoveCore (int index)
+ {
+ int idx = IndexOf (index);
+ if (idx == -1)
+ return false;
+
+ selection.RemoveAt (idx);
+ owner.InvalidateItem (index);
+
+ // UIA Framework event: Selected item removed from selection
+ OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, index));
+
+ return true;
+ }
+
+ int IList.Add (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Clear ()
+ {
+ throw new NotSupportedException ();
+ }
+
+ bool IList.Contains (object selectedIndex)
+ {
+ return Contains ((int)selectedIndex);
+ }
+
+ int IList.IndexOf (object selectedIndex)
+ {
+ return IndexOf ((int) selectedIndex);
+ }
+
+ void IList.Insert (int index, object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Remove (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.RemoveAt (int index)
+ {
+ throw new NotSupportedException ();
+ }
+
+ object IList.this[int index]{
+ get { return this [index]; }
+ set {throw new NotImplementedException (); }
+ }
+
+ public int IndexOf (int selectedIndex)
+ {
+ CheckSorted ();
+
+ for (int i = 0; i < selection.Count; i++)
+ if ((int)selection [i] == selectedIndex)
+ return i;
+
+ return -1;
+ }
+ #endregion Public Methods
+ internal ArrayList List {
+ get {
+ CheckSorted ();
+ return selection;
+ }
+ }
+
+ void CheckSorted ()
+ {
+ if (sorting_needed) {
+ sorting_needed = false;
+ selection.Sort ();
+ }
+ }
+ }
+
+ public class SelectedObjectCollection : IList, ICollection, IEnumerable
+ {
+ private ListBox owner;
+
+ public SelectedObjectCollection (ListBox owner)
+ {
+ this.owner = owner;
+ }
+
+ #region Public Properties
+ public int Count {
+ get { return owner.selected_indices.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return true; }
+ }
+
+ [Browsable(false)]
+ //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public object this [int index] {
+ get {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException ("Index of out range");
+
+ return owner.items [owner.selected_indices [index]];
+ }
+ set {throw new NotSupportedException ();}
+ }
+
+ bool ICollection.IsSynchronized {
+ get { return true; }
+ }
+
+ object ICollection.SyncRoot {
+ get { return this; }
+ }
+
+ bool IList.IsFixedSize {
+ get { return true; }
+ }
+
+ #endregion Public Properties
+
+ #region Public Methods
+ public void Add (object value)
+ {
+ if (owner.selection_mode == SelectionMode.None)
+ throw new ArgumentException ("Cannot call this method if SelectionMode is SelectionMode.None");
+
+ int idx = owner.items.IndexOf (value);
+ if (idx == -1)
+ return;
+
+ owner.selected_indices.Add (idx);
+ }
+
+ public void Clear ()
+ {
+ owner.selected_indices.Clear ();
+ }
+
+ public bool Contains (object selectedObject)
+ {
+ int idx = owner.items.IndexOf (selectedObject);
+ return idx == -1 ? false : owner.selected_indices.Contains (idx);
+ }
+
+ public void CopyTo (Array destination, int index)
+ {
+ for (int i = 0; i < Count; i++)
+ destination.SetValue (this [i], index++);
+ }
+
+ public void Remove (object value)
+ {
+ if (value == null)
+ return;
+
+ int idx = owner.items.IndexOf (value);
+ if (idx == -1)
+ return;
+
+ owner.selected_indices.Remove (idx);
+ }
+
+ int IList.Add (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Clear ()
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Insert (int index, object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.Remove (object value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ void IList.RemoveAt (int index)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public int IndexOf (object selectedObject)
+ {
+ int idx = owner.items.IndexOf (selectedObject);
+ return idx == -1 ? -1 : owner.selected_indices.IndexOf (idx);
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ //FIXME: write an enumerator that uses selection.GetEnumerator
+ // so that invalidation is write on selection changes
+ object [] items = new object [Count];
+ for (int i = 0; i < Count; i++) {
+ items [i] = owner.items [owner.selected_indices [i]];
+ }
+
+ return items.GetEnumerator ();
+ }
+
+ #endregion Public Methods
+ }
+ }
+}