2006-04-21 Mike Kestner <mkestner@novell.com>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / CheckedListBox.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004-2006 Novell, Inc.
21 //
22 // Authors:
23 //      Jordi Mas i Hernandez, jordi@ximian.com
24 //      Mike Kestner  <mkestner@novell.com>
25 //
26 //
27
28 // COMPLETE
29
30 using System;
31 using System.Drawing;
32 using System.Collections;
33 using System.ComponentModel;
34 using System.Reflection;
35
36 namespace System.Windows.Forms
37 {
38
39         public class CheckedListBox : ListBox
40         {
41                 private CheckedIndexCollection checked_indices;
42                 private CheckedItemCollection checked_items;
43                 private Hashtable check_states = new Hashtable ();
44                 private bool check_onclick = false;
45                 private bool three_dcheckboxes = false;
46                 
47                 public CheckedListBox ()
48                 {
49                         checked_indices = new CheckedIndexCollection (this);
50                         checked_items = new CheckedItemCollection (this);
51                         SetStyle (ControlStyles.ResizeRedraw, true);
52                 }
53
54                 #region events
55                 [Browsable (false)]
56                 [EditorBrowsable (EditorBrowsableState.Never)]
57                 public new event EventHandler Click {
58                         add { base.Click += value; }
59                         remove { base.Click -= value; }
60                 }
61                 
62                 [Browsable (false)]
63                 [EditorBrowsable (EditorBrowsableState.Never)]
64                 public new event EventHandler DataSourceChanged {
65                         add { base.DataSourceChanged += value; }
66                         remove { base.DataSourceChanged -= value; }
67                 }
68                 
69                 [Browsable (false)]
70                 [EditorBrowsable (EditorBrowsableState.Never)]
71                 public new event EventHandler DisplayMemberChanged {
72                         add { base.DisplayMemberChanged += value; }
73                         remove { base.DisplayMemberChanged -= value; }
74                 }
75                 
76                 [Browsable (false)]
77                 [EditorBrowsable (EditorBrowsableState.Never)]
78                 public new event DrawItemEventHandler DrawItem {
79                         add { base.DrawItem += value; }
80                         remove { base.DrawItem -= value; }
81                 }
82
83                 [Browsable (false)]
84                 [EditorBrowsable (EditorBrowsableState.Never)]
85                 public new event MeasureItemEventHandler MeasureItem {
86                         add { base.MeasureItem += value; }
87                         remove { base.MeasureItem -= value; }
88                 }
89                 
90                 [Browsable (false)]
91                 [EditorBrowsable (EditorBrowsableState.Never)]
92                 public new event EventHandler ValueMemberChanged {
93                         add { base.ValueMemberChanged += value; }
94                         remove { base.ValueMemberChanged -= value; }
95                 }
96
97                 public event ItemCheckEventHandler ItemCheck;
98                 #endregion Events
99
100                 #region Public Properties
101                 
102                 [Browsable (false)]
103                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
104                 public CheckedListBox.CheckedIndexCollection CheckedIndices {
105                         get {return checked_indices; }
106                 }
107                                 
108                 [Browsable (false)]
109                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
110                 public CheckedListBox.CheckedItemCollection CheckedItems {
111                         get {return checked_items; }
112                 }
113
114                 [DefaultValue (false)]
115                 public bool CheckOnClick {
116                         get { return check_onclick; }
117                         set { check_onclick = value; }
118                 }
119
120                 protected override CreateParams CreateParams {
121                         get { return base.CreateParams;}
122                 }
123                 
124                 [EditorBrowsable (EditorBrowsableState.Never)]
125                 [Browsable (false)]
126                 public new object DataSource {
127                         get { return base.DataSource; }
128                         // FIXME: docs say you can't use a DataSource with this subclass
129                         set { base.DataSource = value; }
130                 }
131
132                 [EditorBrowsable (EditorBrowsableState.Never)]
133                 [Browsable (false)]
134                 public new string DisplayMember {
135                         get { return base.DisplayMember; }
136                         set { base.DisplayMember = value; }
137                 }
138
139                 [Browsable (false)]
140                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
141                 [EditorBrowsable (EditorBrowsableState.Never)]
142                 public override DrawMode DrawMode {
143                         get { return DrawMode.Normal; }
144                         set { /* Not an exception, but has no effect. */ }
145                 }
146
147                 [Browsable (false)]
148                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
149                 [EditorBrowsable (EditorBrowsableState.Never)]
150                 public override int ItemHeight {
151                         get { return base.ItemHeight; }
152                         set { /* Not an exception, but has no effect. */ }
153                 }
154
155                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
156                 [Localizable (true)]
157                 [Editor ("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
158                 public new CheckedListBox.ObjectCollection Items {
159                         get { return (CheckedListBox.ObjectCollection) base.Items; }
160                 }
161
162                 public override SelectionMode SelectionMode {
163                         get { return base.SelectionMode; }
164                         set {
165                                 if (value == SelectionMode.MultiSimple || value == SelectionMode.MultiExtended)
166                                         throw new InvalidEnumArgumentException ("Multi selection modes not supported");
167
168                                 base.SelectionMode = value;
169                         }
170                 }
171
172                 [DefaultValue (false)]
173                 public bool ThreeDCheckBoxes {
174                         get { return three_dcheckboxes; }
175                         set {
176                                 if (three_dcheckboxes == value)
177                                         return;
178
179                                 three_dcheckboxes = value;
180                                 Refresh ();
181                         }
182                 }
183
184                 [Browsable (false)]
185                 [EditorBrowsable (EditorBrowsableState.Never)]
186                 public new string ValueMember {
187                         get { return base.ValueMember; }
188                         set { base.ValueMember = value; }                       
189                 }
190                 
191                 #endregion Public Properties
192
193                 #region Public Methods
194
195                 protected override AccessibleObject CreateAccessibilityInstance ()
196                 {
197                         return base.CreateAccessibilityInstance ();
198                 }
199                 
200                 protected override ListBox.ObjectCollection CreateItemCollection ()
201                 {
202                         return new ObjectCollection (this);
203                 }
204
205                 public bool GetItemChecked (int index)
206                 {
207                         return check_states.Contains (Items [index]);
208                 }
209                 
210                 public CheckState GetItemCheckState (int index)
211                 {
212                         if (index < 0 || index >= Items.Count)
213                                 throw new ArgumentOutOfRangeException ("Index of out range");
214
215                         object o = Items [index];
216                         if (check_states.Contains (o))
217                                 return (CheckState) check_states [o];
218                         else
219                                 return CheckState.Unchecked;
220                 }
221
222                 protected override void OnBackColorChanged (EventArgs e)
223                 {
224                         base.OnBackColorChanged (e);
225                 }
226
227                 protected override void OnClick (EventArgs e)
228                 {
229                         base.OnClick (e);
230                 }
231                 
232                 protected override void OnDrawItem (DrawItemEventArgs e)
233                 {
234                         if (check_states.Contains (Items [e.Index])) {
235                                 DrawItemState state = e.State | DrawItemState.Checked;
236                                 if (((CheckState) check_states [Items [e.Index]]) == CheckState.Indeterminate)
237                                         state |= DrawItemState.Inactive;
238                                 e = new DrawItemEventArgs (e.Graphics, e.Font, e.Bounds, e.Index, state, e.ForeColor, e.BackColor);
239                         }
240                         ThemeEngine.Current.DrawCheckedListBoxItem (this, e);
241                 }
242
243                 protected override void OnFontChanged (EventArgs e)
244                 {
245                         base.OnFontChanged (e);
246                 }
247
248                 protected override void OnHandleCreated (EventArgs e)
249                 {
250                         base.OnHandleCreated (e);
251                 }
252
253                 protected virtual void OnItemCheck (ItemCheckEventArgs ice)
254                 {
255                         if (ItemCheck != null)
256                                 ItemCheck (this, ice);
257                 }
258
259                 protected override void OnKeyPress (KeyPressEventArgs e)
260                 {
261                         base.OnKeyPress (e);
262                         
263                         if (e.KeyChar == ' ' && FocusedItem != -1)
264                                 SetItemChecked (FocusedItem, !GetItemChecked (FocusedItem));
265                 }
266
267                 protected override void OnMeasureItem (MeasureItemEventArgs e)
268                 {
269                         base.OnMeasureItem (e);
270                 }
271
272                 protected override void OnSelectedIndexChanged (EventArgs e)
273                 {
274                         base.OnSelectedIndexChanged (e);
275                 }
276
277                 public void SetItemChecked (int index, bool value)
278                 {
279                         SetItemCheckState (index, value ? CheckState.Checked : CheckState.Unchecked);
280                 }
281
282                 public void SetItemCheckState (int index, CheckState value)
283                 {
284                         if (index < 0 || index >= Items.Count)
285                                 throw new ArgumentOutOfRangeException ("Index of out range");
286
287                         if (!Enum.IsDefined (typeof (CheckState), value))
288                                 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for CheckState", value));
289
290                         CheckState old_value = GetItemCheckState (index);
291                         
292                         if (old_value == value)
293                                 return;
294                         
295                         OnItemCheck (new ItemCheckEventArgs (index, value, old_value));
296
297                         switch (value) {
298                         case CheckState.Checked:
299                         case CheckState.Indeterminate:
300                                 check_states [Items[index]] = value;
301                                 break;
302                         case CheckState.Unchecked:
303                                 check_states.Remove (Items[index]);
304                                 break;
305                         default:
306                                 break;
307                         }
308
309                         UpdateCollections ();
310
311                         InvalidateItem (index);
312                 }
313
314                 protected override void WmReflectCommand (ref Message m)
315                 {
316                         base.WmReflectCommand (ref m);
317                 }
318
319                 protected override void WndProc (ref Message m)
320                 {
321                         base.WndProc (ref m);
322                 }
323
324                 #endregion Public Methods
325
326                 #region Private Methods
327
328                 internal override void OnItemClick (int index)
329                 {                       
330                         if (GetItemChecked (index))
331                                 SetItemCheckState (index, CheckState.Unchecked);
332                         else if (CheckOnClick || SelectedIndices.Contains (index))
333                                 SetItemCheckState (index, CheckState.Checked);
334                         
335                         base.OnItemClick (index);
336                 }
337
338                 internal override void CollectionChanged ()
339                 {
340                         base.CollectionChanged ();
341                         UpdateCollections ();
342                 }
343
344                 private void UpdateCollections ()
345                 {
346                         CheckedItems.Refresh ();
347                         CheckedIndices.Refresh ();
348                 }
349
350                 #endregion Private Methods
351
352                 public class ObjectCollection : ListBox.ObjectCollection
353                 {               
354                         private CheckedListBox owner;
355
356                         public ObjectCollection (CheckedListBox owner) : base (owner)
357                         {
358                                 this.owner = owner;                             
359                         }
360
361                         public int Add (object item, bool isChecked)
362                         {
363                                 return Add (item, isChecked ? CheckState.Checked : CheckState.Unchecked);
364                         }
365                         
366                         public int Add (object item, CheckState check)
367                         {
368                                 Add (item);
369                                 if (check != CheckState.Unchecked)
370                                         owner.check_states [item] = check;
371                                 if (check == CheckState.Checked)
372                                         owner.OnItemCheck (new ItemCheckEventArgs (Count, check, CheckState.Unchecked));
373                                 return Count;
374                         }
375                 }
376
377                 public class CheckedIndexCollection : IList, ICollection, IEnumerable
378                 {
379                         private CheckedListBox owner;
380                         private ArrayList indices = new ArrayList ();
381
382                         internal CheckedIndexCollection (CheckedListBox owner)
383                         {
384                                 this.owner = owner;
385                         }
386
387                         #region Public Properties
388                         public int Count {
389                                 get { return indices.Count; }
390                         }
391
392                         public bool IsReadOnly {
393                                 get { return true;}
394                         }
395
396                         bool ICollection.IsSynchronized {
397                                 get { return false; }
398                         }
399
400                         bool IList.IsFixedSize{
401                                 get { return true; }
402                         }
403
404                         object ICollection.SyncRoot {
405                                 get { return this; }
406                         }
407
408                         [Browsable (false)]
409                         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
410                         public int this[int index] {
411                                 get {
412                                         if (index < 0 || index >= Count)
413                                                 throw new ArgumentOutOfRangeException ("Index of out range");
414
415                                         return (int) indices[index];
416                                 }
417                         }
418                         #endregion Public Properties
419
420                         public bool Contains (int index)
421                         {
422                                 return indices.Contains (index);
423                         }
424
425
426                         public void CopyTo (Array dest, int index)
427                         {
428                                 indices.CopyTo (dest, index);
429                         }
430
431                         public IEnumerator GetEnumerator ()
432                         {
433                                 return indices.GetEnumerator ();
434                         }
435
436                         int IList.Add (object value)
437                         {
438                                 throw new NotSupportedException ();
439                         }
440
441                         void IList.Clear ()
442                         {
443                                 throw new NotSupportedException ();
444                         }
445
446                         bool IList.Contains (object index)
447                         {
448                                 return Contains ((int)index);
449                         }
450
451                         int IList.IndexOf (object index)
452                         {
453                                 return IndexOf ((int) index);
454                         }
455
456                         void IList.Insert (int index, object value)
457                         {
458                                 throw new NotSupportedException ();
459                         }
460
461                         void IList.Remove (object value)
462                         {
463                                 throw new NotSupportedException ();
464                         }
465
466                         void IList.RemoveAt (int index)
467                         {
468                                 throw new NotSupportedException ();
469                         }
470
471                         object IList.this[int index]{
472                                 get {return indices[index]; }
473                                 set {throw new NotImplementedException (); }
474                         }
475
476                         public int IndexOf (int index)
477                         {
478                                 return indices.IndexOf (index);
479                         }
480
481                         #region Private Methods
482                         internal void Refresh ()
483                         {
484                                 indices.Clear ();
485                                 for (int i = 0; i < owner.Items.Count; i++)
486                                         if (owner.check_states.Contains (owner.Items [i]))
487                                                 indices.Add (i);
488                         }
489                         #endregion Private Methods
490
491                 }
492
493                 public class CheckedItemCollection : IList, ICollection, IEnumerable
494                 {
495                         private CheckedListBox owner;
496                         private ArrayList list = new ArrayList ();
497
498                         internal CheckedItemCollection (CheckedListBox owner)
499                         {
500                                 this.owner = owner;
501                         }
502
503                         #region Public Properties
504                         public int Count {
505                                 get { return list.Count; }
506                         }
507
508                         public bool IsReadOnly {
509                                 get { return true; }
510                         }
511
512                         [Browsable (false)]
513                         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
514                         public object this [int index] {
515                                 get {
516                                         if (index < 0 || index >= Count)
517                                                 throw new ArgumentOutOfRangeException ("Index of out range");
518
519                                         return list[index];
520                                 }
521                                 set {throw new NotSupportedException ();}
522                         }
523
524                         bool ICollection.IsSynchronized {
525                                 get { return true; }
526                         }
527
528                         object ICollection.SyncRoot {
529                                 get { return this; }
530                         }
531
532                         bool IList.IsFixedSize {
533                                 get { return true; }
534                         }
535
536                         #endregion Public Properties
537
538                         #region Public Methods
539                         public bool Contains (object selectedObject)
540                         {
541                                 return list.Contains (selectedObject);
542                         }
543
544                         public void CopyTo (Array dest, int index)
545                         {
546                                 list.CopyTo (dest, index);
547                         }
548
549                         int IList.Add (object value)
550                         {
551                                 throw new NotSupportedException ();
552                         }
553
554                         void IList.Clear ()
555                         {
556                                 throw new NotSupportedException ();
557                         }
558
559                         void IList.Insert (int index, object value)
560                         {
561                                 throw new NotSupportedException ();
562                         }
563
564                         void IList.Remove (object value)
565                         {
566                                 throw new NotSupportedException ();
567                         }
568
569                         void IList.RemoveAt (int index)
570                         {
571                                 throw new NotSupportedException ();
572                         }
573         
574                         public int IndexOf (object item)
575                         {
576                                 return list.IndexOf (item);
577                         }
578
579                         public IEnumerator GetEnumerator ()
580                         {
581                                 return list.GetEnumerator ();
582                         }
583
584                         #endregion Public Methods
585
586                         #region Private Methods
587                         internal void Refresh ()
588                         {
589                                 list.Clear ();
590                                 for (int i = 0; i < owner.Items.Count; i++)
591                                         if (owner.check_states.Contains (owner.Items [i]))
592                                                 list.Add (owner.Items[i]);
593                         }
594                         #endregion Private Methods
595                 }
596 #if NET_2_0
597
598                 public bool UseCompatibleTextRendering {
599                         get {
600                                 return use_compatible_text_rendering;
601                         }
602
603                         set {
604                                 use_compatible_text_rendering = value;
605                         }
606                 }
607 #endif
608
609         }
610 }
611