2007-01-03 Jonathan Pobst <monkey@jpobst.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / CheckedListBox.cs
index a9e1d564c94555f143c6d20c8a5e0bbf49cd3ac3..246b539264bc1e9f4b7f751bd4c09de7c06590e7 100644 (file)
 // 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-2005 Novell, Inc.
+// Copyright (c) 2004-2006 Novell, Inc.
 //
 // Authors:
 //     Jordi Mas i Hernandez, jordi@ximian.com
+//     Mike Kestner  <mkestner@novell.com>
 //
 //
 
@@ -39,45 +40,66 @@ namespace System.Windows.Forms
        {
                private CheckedIndexCollection checked_indices;
                private CheckedItemCollection checked_items;
-               private bool check_onclick;
-               private bool three_dcheckboxes;
+               private Hashtable check_states = new Hashtable ();
+               private bool check_onclick = false;
+               private bool three_dcheckboxes = false;
                
                public CheckedListBox ()
                {
-                       items = new CheckedListBox.ObjectCollection (this);
                        checked_indices = new CheckedIndexCollection (this);
                        checked_items = new CheckedItemCollection (this);
-                       check_onclick = false;
-                       three_dcheckboxes = false;
-                       listbox_info.item_height = FontHeight + 2;
                        SetStyle (ControlStyles.ResizeRedraw, true);
                }
 
                #region events
+               static object ItemCheckEvent = new object ();
+
                [Browsable (false)]
                [EditorBrowsable (EditorBrowsableState.Never)]
-               public new event EventHandler Click;
+               public new event EventHandler Click {
+                       add { base.Click += value; }
+                       remove { base.Click -= value; }
+               }
                
                [Browsable (false)]
                [EditorBrowsable (EditorBrowsableState.Never)]
-               public new event EventHandler DataSourceChanged;
+               public new event EventHandler DataSourceChanged {
+                       add { base.DataSourceChanged += value; }
+                       remove { base.DataSourceChanged -= value; }
+               }
                
                [Browsable (false)]
                [EditorBrowsable (EditorBrowsableState.Never)]
-               public new event EventHandler DisplayMemberChanged;
+               public new event EventHandler DisplayMemberChanged {
+                       add { base.DisplayMemberChanged += value; }
+                       remove { base.DisplayMemberChanged -= value; }
+               }
                
                [Browsable (false)]
                [EditorBrowsable (EditorBrowsableState.Never)]
-               public new event DrawItemEventHandler DrawItem;
-               public event ItemCheckEventHandler ItemCheck;
-               
+               public new event DrawItemEventHandler DrawItem {
+                       add { base.DrawItem += value; }
+                       remove { base.DrawItem -= value; }
+               }
+
                [Browsable (false)]
                [EditorBrowsable (EditorBrowsableState.Never)]
-               public new event MeasureItemEventHandler MeasureItem;
+               public new event MeasureItemEventHandler MeasureItem {
+                       add { base.MeasureItem += value; }
+                       remove { base.MeasureItem -= value; }
+               }
                
                [Browsable (false)]
                [EditorBrowsable (EditorBrowsableState.Never)]
-               public new event EventHandler ValueMemberChanged;
+               public new event EventHandler ValueMemberChanged {
+                       add { base.ValueMemberChanged += value; }
+                       remove { base.ValueMemberChanged -= value; }
+               }
+
+               public event ItemCheckEventHandler ItemCheck {
+                       add { Events.AddHandler (ItemCheckEvent, value); }
+                       remove { Events.RemoveHandler (ItemCheckEvent, value); }
+               }
                #endregion Events
 
                #region Public Properties
@@ -108,6 +130,7 @@ namespace System.Windows.Forms
                [Browsable (false)]
                public new object DataSource {
                        get { return base.DataSource; }
+                       // FIXME: docs say you can't use a DataSource with this subclass
                        set { base.DataSource = value; }
                }
 
@@ -123,15 +146,15 @@ namespace System.Windows.Forms
                [EditorBrowsable (EditorBrowsableState.Never)]
                public override DrawMode DrawMode {
                        get { return DrawMode.Normal; }
-                       set { /* Not possible */ }
+                       set { /* Not an exception, but has no effect. */ }
                }
 
                [Browsable (false)]
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                [EditorBrowsable (EditorBrowsableState.Never)]
                public override int ItemHeight {
-                       get { return listbox_info.item_height; }
-                       set { /* Not possible */ }
+                       get { return base.ItemHeight; }
+                       set { /* Not an exception, but has no effect. */ }
                }
 
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
@@ -144,8 +167,11 @@ namespace System.Windows.Forms
                public override SelectionMode SelectionMode {
                        get { return base.SelectionMode; }
                        set {
+                               if (!Enum.IsDefined (typeof (SelectionMode), value))
+                                       throw new InvalidEnumArgumentException ("value", (int) value, typeof (SelectionMode));
+
                                if (value == SelectionMode.MultiSimple || value == SelectionMode.MultiExtended)
-                                       throw new InvalidEnumArgumentException ("Multi selection modes not supported");
+                                       throw new ArgumentException ("Multi selection not supported on CheckedListBox");
 
                                base.SelectionMode = value;
                        }
@@ -186,7 +212,7 @@ namespace System.Windows.Forms
 
                public bool GetItemChecked (int index)
                {
-                       return (GetItemCheckState (index) != CheckState.Unchecked);
+                       return check_states.Contains (Items [index]);
                }
                
                public CheckState GetItemCheckState (int index)
@@ -194,7 +220,11 @@ namespace System.Windows.Forms
                        if (index < 0 || index >= Items.Count)
                                throw new ArgumentOutOfRangeException ("Index of out range");
 
-                       return (Items.GetListBoxItem (index)).State;
+                       object o = Items [index];
+                       if (check_states.Contains (o))
+                               return (CheckState) check_states [o];
+                       else
+                               return CheckState.Unchecked;
                }
 
                protected override void OnBackColorChanged (EventArgs e)
@@ -205,13 +235,16 @@ namespace System.Windows.Forms
                protected override void OnClick (EventArgs e)
                {
                        base.OnClick (e);
-                       
-                       if (Click != null)                      
-                               Click (this, EventArgs.Empty);
                }
                
                protected override void OnDrawItem (DrawItemEventArgs e)
                {
+                       if (check_states.Contains (Items [e.Index])) {
+                               DrawItemState state = e.State | DrawItemState.Checked;
+                               if (((CheckState) check_states [Items [e.Index]]) == CheckState.Indeterminate)
+                                       state |= DrawItemState.Inactive;
+                               e = new DrawItemEventArgs (e.Graphics, e.Font, e.Bounds, e.Index, state, e.ForeColor, e.BackColor);
+                       }
                        ThemeEngine.Current.DrawCheckedListBoxItem (this, e);
                }
 
@@ -227,25 +260,22 @@ namespace System.Windows.Forms
 
                protected virtual void OnItemCheck (ItemCheckEventArgs ice)
                {
-                       if (ItemCheck != null)
-                               ItemCheck (this, ice);
+                       ItemCheckEventHandler eh = (ItemCheckEventHandler)(Events [ItemCheckEvent]);
+                       if (eh != null)
+                               eh (this, ice);
                }
 
                protected override void OnKeyPress (KeyPressEventArgs e)
                {
                        base.OnKeyPress (e);
                        
-                       if (e.KeyChar == ' ') { // Space should check focused item                              
-                               if (focused_item != -1) {
-                                       SetItemChecked (focused_item, !GetItemChecked (focused_item));
-                               }
-                       }
+                       if (e.KeyChar == ' ' && FocusedItem != -1)
+                               SetItemChecked (FocusedItem, !GetItemChecked (FocusedItem));
                }
 
                protected override void OnMeasureItem (MeasureItemEventArgs e)
                {
-                       if (MeasureItem != null)
-                               MeasureItem (this, e);
+                       base.OnMeasureItem (e);
                }
 
                protected override void OnSelectedIndexChanged (EventArgs e)
@@ -266,33 +296,28 @@ namespace System.Windows.Forms
                        if (!Enum.IsDefined (typeof (CheckState), value))
                                throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for CheckState", value));
 
-                       CheckState old_value = (Items.GetListBoxItem (index)).State;
+                       CheckState old_value = GetItemCheckState (index);
                        
                        if (old_value == value)
                                return;
                        
-                       (Items.GetListBoxItem (index)).State = value;
-
-                       Rectangle invalidate = GetItemDisplayRectangle (index, LBoxInfo.top_item);
+                       OnItemCheck (new ItemCheckEventArgs (index, value, old_value));
 
                        switch (value) {
-                               case CheckState.Checked:
-                                       checked_indices.AddIndex (index);
-                                       checked_items.AddObject (Items[index]);
-                                       break;
-                               case CheckState.Unchecked:
-                                       checked_indices.RemoveIndex (index);
-                                       checked_items.RemoveObject (Items[index]);
-                                       break;
-                               case CheckState.Indeterminate:
-                               default:
-                                       break;
+                       case CheckState.Checked:
+                       case CheckState.Indeterminate:
+                               check_states [Items[index]] = value;
+                               break;
+                       case CheckState.Unchecked:
+                               check_states.Remove (Items[index]);
+                               break;
+                       default:
+                               break;
                        }
 
-                       OnItemCheck (new ItemCheckEventArgs (index, value, old_value));
+                       UpdateCollections ();
 
-                       if (ClientRectangle.Contains (invalidate))
-                               Invalidate (invalidate);
+                       InvalidateCheckbox (index);
                }
 
                protected override void WmReflectCommand (ref Message m)
@@ -309,59 +334,46 @@ namespace System.Windows.Forms
 
                #region Private Methods
 
-               internal override void OnMouseDownLB (object sender, MouseEventArgs e)
-               {                       
-                       Rectangle hit_rect;
-                       CheckState value =  CheckState.Checked;
-                       bool set_value = false;
-                       int index = IndexFromPointDisplayRectangle (e.X, e.Y);
-
-                       if (Click != null) {
-                               if (e.Button == MouseButtons.Left) {
-                                       Click (this, e);
-                               }
-                       }       
+               int last_clicked_index = -1;
 
-                       if (index == -1)
-                               return;
-                       
-                       /* CheckBox hit */
-                       hit_rect = GetItemDisplayRectangle (index, LBoxInfo.top_item); // Full item rect
-                       hit_rect.X += ThemeEngine.Current.CheckedListBoxCheckRectangle().X;
-                       hit_rect.Y += ThemeEngine.Current.CheckedListBoxCheckRectangle().Y;
-                       hit_rect.Width = ThemeEngine.Current.CheckedListBoxCheckRectangle().Width;
-                       hit_rect.Height = ThemeEngine.Current.CheckedListBoxCheckRectangle().Height;
-                       
-                       if ((Items.GetListBoxItem (index)).State == CheckState.Checked) { //From checked to unchecked
-                               value = CheckState.Unchecked;
-                               set_value = true;
-                       } else {
-                               
-                               if (check_onclick) {
-                                       set_value = true;
-                               } else {
-                                       if ((Items.GetListBoxItem (index)).Selected == true)
-                                               set_value = true;
-                               }
+               internal override void OnItemClick (int index)
+               {                       
+                       if (CheckOnClick || last_clicked_index == index) {
+                               if (GetItemChecked (index))
+                                       SetItemCheckState (index, CheckState.Unchecked);
+                               else
+                                       SetItemCheckState (index, CheckState.Checked);
                        }
-
-                       if (set_value)
-                               SetItemCheckState (index, value);
                        
-                       SelectedItemFromNavigation (index);
-                       SetFocusedItem (index);
+                       last_clicked_index = index;
+                       base.OnItemClick (index);
+               }
+
+               internal override void CollectionChanged ()
+               {
+                       base.CollectionChanged ();
+                       UpdateCollections ();
                }
 
-               internal override void UpdateItemInfo (UpdateOperation operation, int first, int last)
+               private void InvalidateCheckbox (int index)
                {
-                       base.UpdateItemInfo (operation, first, last);
-                       CheckedItems.ReCreate ();
-                       CheckedIndices.ReCreate ();
+                       Rectangle area = GetItemDisplayRectangle (index, TopIndex);
+                       area.X += 2;
+                       area.Y += (area.Height - 11) / 2;
+                       area.Width = 11;
+                       area.Height = 11;
+                       Invalidate (area);
+               }
+
+               private void UpdateCollections ()
+               {
+                       CheckedItems.Refresh ();
+                       CheckedIndices.Refresh ();
                }
 
                #endregion Private Methods
 
-               public class ObjectCollection : ListBox.ObjectCollection
+               public new class ObjectCollection : ListBox.ObjectCollection
                {               
                        private CheckedListBox owner;
 
@@ -370,32 +382,23 @@ namespace System.Windows.Forms
                                this.owner = owner;                             
                        }
 
-                       public int Add (object item,  bool isChecked)
+                       public int Add (object item, bool isChecked)
                        {
-                               if (isChecked)
-                                       return Add (item, CheckState.Checked);
-                               
-                               return Add (item, CheckState.Unchecked);
-                                       
+                               return Add (item, isChecked ? CheckState.Checked : CheckState.Unchecked);
                        }
                        
                        public int Add (object item, CheckState check)
                        {
-                               int cnt = object_items.Count;
-                               ListBox.ListBoxItem box_item = new ListBox.ListBoxItem (cnt);
-                               box_item.State = check;
-                               object_items.Add (item);
-                               listbox_items.Add (box_item);
+                               Add (item);
+                               if (check != CheckState.Unchecked)
+                                       owner.check_states [item] = check;
                                if (check == CheckState.Checked)
-                                       owner.OnItemCheck (new ItemCheckEventArgs (cnt, check, CheckState.Unchecked));
-                               owner.UpdateItemInfo (UpdateOperation.AddItems, cnt, cnt);
-                               return cnt;
+                                       owner.OnItemCheck (new ItemCheckEventArgs (Count-1, check, CheckState.Unchecked));
+                               owner.UpdateCollections ();
+                               return Count - 1;
                        }
                }
 
-               /*
-                       CheckedListBox.CheckedIndexCollection
-               */
                public class CheckedIndexCollection : IList, ICollection, IEnumerable
                {
                        private CheckedListBox owner;
@@ -407,11 +410,11 @@ namespace System.Windows.Forms
                        }
 
                        #region Public Properties
-                       public virtual int Count {
+                       public int Count {
                                get { return indices.Count; }
                        }
 
-                       public virtual bool IsReadOnly {
+                       public bool IsReadOnly {
                                get { return true;}
                        }
 
@@ -445,12 +448,12 @@ namespace System.Windows.Forms
                        }
 
 
-                       public virtual void CopyTo (Array dest, int index)
+                       public void CopyTo (Array dest, int index)
                        {
                                indices.CopyTo (dest, index);
                        }
 
-                       public virtual IEnumerator GetEnumerator ()
+                       public IEnumerator GetEnumerator ()
                        {
                                return indices.GetEnumerator ();
                        }
@@ -501,44 +504,21 @@ namespace System.Windows.Forms
                        }
 
                        #region Private Methods
-
-                       internal void AddIndex (int index)
-                       {
-                               indices.Add (index);
-                       }
-
-                       internal void ClearIndices ()
+                       internal void Refresh ()
                        {
                                indices.Clear ();
+                               for (int i = 0; i < owner.Items.Count; i++)
+                                       if (owner.check_states.Contains (owner.Items [i]))
+                                               indices.Add (i);
                        }
-
-                       internal void RemoveIndex (int index)
-                       {
-                               indices.Remove (index);
-                       }
-
-                       internal void ReCreate ()
-                       {
-                               indices.Clear ();
-
-                               for (int i = 0; i < owner.Items.Count; i++) {
-                                       ListBox.ListBoxItem item = owner.Items.GetListBoxItem (i);
-
-                                       if (item.State == CheckState.Checked)
-                                               indices.Add (item.Index);
-                               }
-                       }
-
                        #endregion Private Methods
+
                }
 
-               /*
-                       CheckedItemCollection
-               */
                public class CheckedItemCollection : IList, ICollection, IEnumerable
                {
                        private CheckedListBox owner;
-                       private ArrayList object_items = new ArrayList ();
+                       private ArrayList list = new ArrayList ();
 
                        internal CheckedItemCollection (CheckedListBox owner)
                        {
@@ -546,22 +526,22 @@ namespace System.Windows.Forms
                        }
 
                        #region Public Properties
-                       public virtual int Count {
-                               get { return object_items.Count; }
+                       public int Count {
+                               get { return list.Count; }
                        }
 
-                       public virtual bool IsReadOnly {
+                       public bool IsReadOnly {
                                get { return true; }
                        }
 
                        [Browsable (false)]
                        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
-                       public virtual object this [int index] {
+                       public object this [int index] {
                                get {
                                        if (index < 0 || index >= Count)
                                                throw new ArgumentOutOfRangeException ("Index of out range");
 
-                                       return object_items[index];
+                                       return list[index];
                                }
                                set {throw new NotSupportedException ();}
                        }
@@ -578,22 +558,17 @@ namespace System.Windows.Forms
                                get { return true; }
                        }
 
-                       object IList.this[int index] {
-                               get { return object_items[index]; }
-                               set { throw new NotSupportedException (); }
-                       }
-
                        #endregion Public Properties
 
                        #region Public Methods
-                       public virtual bool Contains (object selectedObject)
+                       public bool Contains (object selectedObject)
                        {
-                               return object_items.Contains (selectedObject);
+                               return list.Contains (selectedObject);
                        }
 
-                       public virtual void CopyTo (Array dest, int index)
+                       public void CopyTo (Array dest, int index)
                        {
-                               object_items.CopyTo (dest, index);
+                               list.CopyTo (dest, index);
                        }
 
                        int IList.Add (object value)
@@ -606,11 +581,6 @@ namespace System.Windows.Forms
                                throw new NotSupportedException ();
                        }
 
-                       bool IList.Contains (object selectedIndex)
-                       {
-                               throw new NotImplementedException ();
-                       }
-                               
                        void IList.Insert (int index, object value)
                        {
                                throw new NotSupportedException ();
@@ -628,45 +598,39 @@ namespace System.Windows.Forms
        
                        public int IndexOf (object item)
                        {
-                               return object_items.IndexOf (item);
+                               return list.IndexOf (item);
                        }
 
-                       public virtual IEnumerator GetEnumerator ()
+                       public IEnumerator GetEnumerator ()
                        {
-                               return object_items.GetEnumerator ();
+                               return list.GetEnumerator ();
                        }
 
                        #endregion Public Methods
 
                        #region Private Methods
-                       internal void AddObject (object obj)
+                       internal void Refresh ()
                        {
-                               object_items.Add (obj);
+                               list.Clear ();
+                               for (int i = 0; i < owner.Items.Count; i++)
+                                       if (owner.check_states.Contains (owner.Items [i]))
+                                               list.Add (owner.Items[i]);
                        }
+                       #endregion Private Methods
+               }
+#if NET_2_0
 
-                       internal void ClearObjects ()
-                       {
-                               object_items.Clear ();
-                       }
-
-                       internal void ReCreate ()
-                       {
-                               object_items.Clear ();
-
-                               for (int i = 0; i < owner.Items.Count; i++) {
-                                       ListBox.ListBoxItem item = owner.Items.GetListBoxItem (i);
-
-                                       if (item.State == CheckState.Checked)
-                                               object_items.Add (owner.Items[item.Index]);
-                               }
+               public bool UseCompatibleTextRendering {
+                       get {
+                               return use_compatible_text_rendering;
                        }
 
-                       internal void RemoveObject (object obj)
-                       {
-                               object_items.Remove (obj);
+                       set {
+                               use_compatible_text_rendering = value;
                        }
-                       #endregion Private Methods
                }
+#endif
+
        }
 }