Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / DataGridViewComboBoxCell.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) 2005 Novell, Inc. (http://www.novell.com)
21 //
22 // Author:
23 //      Pedro Martínez Juliá <pedromj@gmail.com>
24 //      Ivan N. Zlatev <contact@i-nz.net>
25 //
26
27
28 using System.Collections;
29 using System.ComponentModel;
30 using System.Drawing;
31
32 namespace System.Windows.Forms {
33
34         public class DataGridViewComboBoxCell : DataGridViewCell {
35
36                 private bool autoComplete;
37                 private object dataSource;
38                 private string displayMember;
39                 private DataGridViewComboBoxDisplayStyle displayStyle;
40                 private bool displayStyleForCurrentCellOnly;
41                 private int dropDownWidth;
42                 private FlatStyle flatStyle;
43                 private ObjectCollection items;
44                 private int maxDropDownItems;
45                 private bool sorted;
46                 private string valueMember;
47                 private DataGridViewComboBoxColumn owningColumnTemlate;
48
49                 public DataGridViewComboBoxCell () : base() {
50                         autoComplete = true;
51                         dataSource = null;
52                         displayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton;
53                         displayStyleForCurrentCellOnly = false;
54                         dropDownWidth = 1;
55                         flatStyle = FlatStyle.Standard;
56                         items = new ObjectCollection(this);
57                         maxDropDownItems = 8;
58                         sorted = false;
59                         owningColumnTemlate = null;
60                 }
61
62                 [DefaultValue (true)]
63                 public virtual bool AutoComplete {
64                         get { return autoComplete; }
65                         set { autoComplete = value; }
66                 }
67
68                 public virtual object DataSource {
69                         get { return dataSource; }
70                         set {
71                                 if (value is IList || value is IListSource || value == null) {
72                                         dataSource = value;
73                                         return;
74                                 }
75                                 throw new Exception("Value is no IList, IListSource or null.");
76                         }
77                 }
78
79                 [DefaultValue ("")]
80                 public virtual string DisplayMember {
81                         get { return displayMember; }
82                         set { displayMember = value; }
83                 }
84
85                 [DefaultValue (DataGridViewComboBoxDisplayStyle.DropDownButton)]
86                 public DataGridViewComboBoxDisplayStyle DisplayStyle {
87                         get { return displayStyle; }
88                         set { displayStyle = value; }
89                 }
90
91                 [DefaultValue (false)]
92                 public bool DisplayStyleForCurrentCellOnly {
93                         get { return displayStyleForCurrentCellOnly; }
94                         set { displayStyleForCurrentCellOnly = value; }
95                 }
96
97                 [DefaultValue (1)]
98                 public virtual int DropDownWidth {
99                         get { return dropDownWidth; }
100                         set {
101                                 if (value < 1) {
102                                         throw new ArgumentOutOfRangeException("Value is less than 1.");
103                                 }
104                                 dropDownWidth = value;
105                         }
106                 }
107
108                 public override Type EditType {
109                         get { return typeof(DataGridViewComboBoxEditingControl); }
110                 }
111
112                 [DefaultValue (FlatStyle.Standard)]
113                 public FlatStyle FlatStyle {
114                         get { return flatStyle; }
115                         set {
116                                 if (!Enum.IsDefined(typeof(FlatStyle), value)) {
117                                         throw new InvalidEnumArgumentException("Value is not valid FlatStyle.");
118                                 }
119                                 flatStyle = value;
120                         }
121                 }
122
123                 public override Type FormattedValueType {
124                         get { return typeof(string); }
125                 }
126
127                 [Browsable (false)]
128                 public virtual ObjectCollection Items {
129                         get {
130                                 if (DataGridView != null && DataGridView.BindingContext != null 
131                                     && DataSource != null && !String.IsNullOrEmpty (ValueMember)) {
132                                         items.ClearInternal ();
133                                         CurrencyManager dataManager = (CurrencyManager) DataGridView.BindingContext[DataSource];
134                                         if (dataManager != null && dataManager.Count > 0) {
135                                                 foreach (object item in dataManager.List)
136                                                         items.AddInternal (item);
137                                         }
138                                 }
139
140                                 return items;
141                         }
142                 }
143
144                 [DefaultValue (8)]
145                 public virtual int MaxDropDownItems {
146                         get { return maxDropDownItems; }
147                         set {
148                                 if (value < 1 || value > 100) {
149                                         throw new ArgumentOutOfRangeException("Value is less than 1 or greater than 100.");
150                                 }
151                                 maxDropDownItems = value;
152                         }
153                 }
154
155                 [DefaultValue (false)]
156                 public virtual bool Sorted {
157                         get { return sorted; }
158                         set {
159                                 /*
160                                 if () {
161                                         throw new ArgumentException("Cannot sort a cell attached to a data source.");
162                                 }
163                                 */
164                                 sorted = value;
165                         }
166                 }
167
168                 [DefaultValue ("")]
169                 public virtual string ValueMember {
170                         get { return valueMember; }
171                         set { valueMember = value; }
172                 }
173
174                 public override Type ValueType {
175                         get { return typeof(string); }
176                 }
177
178                 // Valid only for template Cells and used as a bridge to push items
179                 internal DataGridViewComboBoxColumn OwningColumnTemplate {
180                         get { return owningColumnTemlate; }
181                         set { owningColumnTemlate = value; }
182                 }
183
184                 public override object Clone () {
185                         DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell) base.Clone();
186                         cell.autoComplete = this.autoComplete;
187                         cell.dataSource = this.dataSource;
188                         cell.displayStyle = this.displayStyle;
189                         cell.displayMember = this.displayMember;
190                         cell.valueMember = this.valueMember;
191                         cell.displayStyleForCurrentCellOnly = this.displayStyleForCurrentCellOnly;
192                         cell.dropDownWidth = this.dropDownWidth;
193                         cell.flatStyle = this.flatStyle;
194                         cell.items.AddRangeInternal(this.items);
195                         cell.maxDropDownItems = this.maxDropDownItems;
196                         cell.sorted = this.sorted;
197                         return cell;
198                 }
199
200                 public override void DetachEditingControl () {
201                         this.DataGridView.EditingControlInternal = null;
202                 }
203
204                 public override void InitializeEditingControl (int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) {
205                         base.InitializeEditingControl (rowIndex, initialFormattedValue, dataGridViewCellStyle);
206                         
207                         ComboBox editingControl = DataGridView.EditingControl as ComboBox;
208                         
209                         editingControl.DropDownStyle = ComboBoxStyle.DropDownList;
210                         editingControl.Sorted = Sorted;
211                         editingControl.DataSource = null;
212                         editingControl.ValueMember = null;
213                         editingControl.DisplayMember = null;
214                         editingControl.Items.Clear();
215                         editingControl.SelectedIndex = -1;
216
217                         if (DataSource != null) {
218                                 editingControl.DataSource = DataSource;
219                                 editingControl.ValueMember = ValueMember;
220                                 editingControl.DisplayMember = DisplayMember;
221                         } else {
222                                 editingControl.Items.AddRange (this.Items);
223                                 if (initialFormattedValue != null && editingControl.Items.IndexOf (initialFormattedValue) != -1)
224                                         editingControl.SelectedItem = initialFormattedValue;
225                         }
226                 }
227
228                 internal void SyncItems ()
229                 {
230                         if (DataSource != null || OwningColumnTemplate == null)
231                                 return;
232
233                         if (OwningColumnTemplate.DataGridView != null) {
234                                 DataGridViewComboBoxEditingControl editor = OwningColumnTemplate.DataGridView.EditingControl
235                                                                             as DataGridViewComboBoxEditingControl;
236                                 if (editor != null) {
237                                         object selectedItem = editor.SelectedItem;
238                                         editor.Items.Clear ();
239                                         editor.Items.AddRange (items);
240                                         if (editor.Items.IndexOf (selectedItem) != -1)
241                                                 editor.SelectedItem = selectedItem;
242                                 }
243                         }
244
245                         // Push the new items to the column
246                         OwningColumnTemplate.SyncItems (Items);
247                 }
248
249                 public override bool KeyEntersEditMode (KeyEventArgs e)
250                 {
251                         if (e.KeyCode == Keys.Space)
252                                 return true;
253                         if ((int)e.KeyCode >= 48 && (int)e.KeyCode <= 90)
254                                 return true;
255                         if ((int)e.KeyCode >= 96 && (int)e.KeyCode <= 111)
256                                 return true;
257                         if (e.KeyCode == Keys.BrowserSearch || e.KeyCode == Keys.SelectMedia)
258                                 return true;
259                         if ((int)e.KeyCode >= 186 && (int)e.KeyCode <= 229)
260                                 return true;
261                         if (e.KeyCode == Keys.Attn || e.KeyCode == Keys.Packet)
262                                 return true;
263                         if ((int)e.KeyCode >= 248 && (int)e.KeyCode <= 254)
264                                 return true;
265                         if (e.KeyCode == Keys.F4)
266                                 return true;
267                         if ((e.Modifiers == Keys.Alt) && (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up))
268                                 return true;
269
270                         return false;
271                 }
272
273                 public override object ParseFormattedValue (object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)
274                 {
275                         return base.ParseFormattedValue (formattedValue, cellStyle, formattedValueTypeConverter, valueTypeConverter);
276                 }
277
278                 public override string ToString () {
279                         return string.Format ("DataGridViewComboBoxCell {{ ColumnIndex={0}, RowIndex={1} }}", ColumnIndex, RowIndex);
280                 }
281
282                 protected override Rectangle GetContentBounds (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)
283                 {
284                         if (DataGridView == null)
285                                 return Rectangle.Empty;
286
287                         object o = FormattedValue;
288                         Size s = Size.Empty;
289
290                         if (o != null)
291                                 s = DataGridViewCell.MeasureTextSize (graphics, o.ToString (), cellStyle.Font, TextFormatFlags.Default);
292
293                         return new Rectangle (1, (OwningRow.Height - s.Height) / 2, s.Width - 3, s.Height);
294                 }
295
296                 protected override Rectangle GetErrorIconBounds (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex)
297                 {
298                         if (DataGridView == null || string.IsNullOrEmpty (ErrorText))
299                                 return Rectangle.Empty;
300
301                         Size error_icon = new Size (12, 11);
302                         return new Rectangle (new Point (Size.Width - error_icon.Width - 23, (Size.Height - error_icon.Height) / 2), error_icon);
303                 }
304
305                 protected override object GetFormattedValue (object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
306                 {
307                         return base.GetFormattedValue (value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context);
308                 }
309
310                 protected override Size GetPreferredSize (Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize)
311                 {
312                         object o = FormattedValue;
313
314                         if (o != null) {
315                                 Size s = DataGridViewCell.MeasureTextSize (graphics, o.ToString (), cellStyle.Font, TextFormatFlags.Default);
316                                 s.Height = Math.Max (s.Height, 22);
317                                 s.Width += 25;
318                                 return s;
319                         } else
320                                 return new Size (39, 22);
321                 }
322
323                 protected override void OnDataGridViewChanged () {
324                         // Here we're supposed to do something with DataSource, etc, according to MSDN.
325                         base.OnDataGridViewChanged ();
326                 }
327
328                 protected override void OnEnter (int rowIndex, bool throughMouseClick) {
329                         base.OnEnter (rowIndex, throughMouseClick);
330                 }
331
332                 protected override void OnLeave (int rowIndex, bool throughMouseClick) {
333                         base.OnLeave (rowIndex, throughMouseClick);
334                 }
335
336                 protected override void OnMouseDown (DataGridViewCellMouseEventArgs e) {
337                         base.OnMouseDown (e);
338
339                         if (!ReadOnly)
340                         {
341                                 // Any mouse-click on the cell should be passed along to any
342                                 // combo-box control.
343                                 if (IsInEditMode)
344                                 {
345                                         DataGridViewComboBoxEditingControl cb
346                                                 = (DataGridView.EditingControl
347                                                         as DataGridViewComboBoxEditingControl);
348                                         if (cb != null)
349                                                 cb.OnMouseDownInternal (e);
350                                 }
351                         }
352                 }
353
354                 protected override void OnMouseClick (DataGridViewCellMouseEventArgs e) {
355                         base.OnMouseClick (e);
356                 }
357
358                 protected override void OnMouseEnter (int rowIndex) {
359                         base.OnMouseEnter (rowIndex);
360                 }
361
362                 protected override void OnMouseLeave (int rowIndex) {
363                         base.OnMouseLeave (rowIndex);
364                 }
365
366                 protected override void OnMouseMove (DataGridViewCellMouseEventArgs e) {
367                         //Console.WriteLine ("MouseMove (Location: {0}", e.Location);
368                         base.OnMouseMove (e);
369                 }
370
371                 protected override void Paint (Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
372                 {
373                         // The internal paint routines are overridden instead of
374                         // doing the custom paint logic here
375                         base.Paint (graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
376                 }
377
378                 internal override void PaintPartContent (Graphics graphics, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, DataGridViewCellStyle cellStyle, object formattedValue)
379                 {
380                         Color color = Selected ? cellStyle.SelectionForeColor : cellStyle.ForeColor;
381                         TextFormatFlags flags = TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter | TextFormatFlags.TextBoxControl;
382         
383                         Rectangle text_area = ContentBounds;
384                         text_area.X += cellBounds.X;
385                         text_area.Y += cellBounds.Y;
386
387                         Rectangle button_area = CalculateButtonArea (cellBounds);
388
389                         // The background of the dropdown button should be gray, not
390                         // the background color of the cell.
391                         graphics.FillRectangle (SystemBrushes.Control, button_area);
392                         ThemeEngine.Current.CPDrawComboButton (graphics, button_area, ButtonState.Normal);
393
394                         if (formattedValue != null)
395                                 TextRenderer.DrawText (graphics, formattedValue.ToString (), cellStyle.Font, text_area, color, flags);
396                 }
397                 
398                 private Rectangle CalculateButtonArea (Rectangle cellBounds)
399                 {
400                         Rectangle button_area, text_area;
401                         int border = ThemeEngine.Current.Border3DSize.Width;
402                         const int button_width = 16;
403
404                         text_area = cellBounds;
405
406                         button_area = cellBounds;
407                         button_area.X = text_area.Right - button_width - border;
408                         button_area.Y = text_area.Y + border;
409                         button_area.Width = button_width;
410                         button_area.Height = text_area.Height - 2 * border;
411                         
412                         return button_area;
413                 }
414
415                 // IMPORTANT: Only call the internal methods from within DataGridViewComboBoxCell
416                 // for adding/removing/clearing because the other methods invoke an update of the 
417                 // column items collection and you might end up in an endless loop.
418                 //
419                 [ListBindable (false)]
420                 public class ObjectCollection : IList, ICollection, IEnumerable {
421
422                         private ArrayList list;
423                         private DataGridViewComboBoxCell owner;
424
425                         public ObjectCollection (DataGridViewComboBoxCell owner)
426                         {
427                                 this.owner = owner;
428                                 list = new ArrayList();
429                         }
430
431                         public int Count {
432                                 get { return list.Count; }
433                         }
434
435                         bool IList.IsFixedSize {
436                                 get { return list.IsFixedSize; }
437                         }
438
439                         public bool IsReadOnly {
440                                 get { return list.IsReadOnly; }
441                         }
442
443                         bool ICollection.IsSynchronized {
444                                 get { return list.IsSynchronized; }
445                         }
446
447                         object ICollection.SyncRoot {
448                                 get { return list.SyncRoot; }
449                         }
450
451                         public virtual object this [int index] {
452                                 get { return list[index]; }
453                                 set {
454                                         ThrowIfOwnerIsDataBound ();
455                                         list[index] = value;
456                                 }
457                         }
458
459                         public int Add (object item)
460                         {
461                                 ThrowIfOwnerIsDataBound ();
462                                 int index = AddInternal (item);
463                                 SyncOwnerItems ();
464                                 return index;
465                         }
466                         
467                         internal int AddInternal (object item)
468                         {
469                                 return list.Add (item);
470                         }
471
472                         internal void AddRangeInternal (ICollection items)
473                         {
474                                 list.AddRange (items);
475                         }
476
477                         public void AddRange (ObjectCollection value)
478                         {
479                                 ThrowIfOwnerIsDataBound ();
480                                 AddRangeInternal (value);
481                                 SyncOwnerItems ();
482                         }
483
484                         private void SyncOwnerItems ()
485                         {
486                                 ThrowIfOwnerIsDataBound ();
487                                 if (owner != null)
488                                         owner.SyncItems ();
489                         }
490
491                         public void ThrowIfOwnerIsDataBound ()
492                         {
493                                 if (owner != null && owner.DataGridView != null && owner.DataSource != null)
494                                         throw new ArgumentException ("Cannot modify collection if the cell is data bound.");
495                         }
496
497                         public void AddRange (params object[] items)
498                         {
499                                 ThrowIfOwnerIsDataBound ();
500                                 AddRangeInternal (items);
501                                 SyncOwnerItems ();
502                         }
503
504                         public void Clear ()
505                         {
506                                 ThrowIfOwnerIsDataBound ();
507                                 ClearInternal ();
508                                 SyncOwnerItems ();
509                         }
510
511                         internal void ClearInternal ()
512                         {
513                                 list.Clear ();
514                         }
515
516                         public bool Contains (object value)
517                         {
518                                 return list.Contains(value);
519                         }
520
521                         void ICollection.CopyTo (Array destination, int index)
522                         {
523                                 CopyTo ((object[]) destination, index);
524                         }
525
526                         public void CopyTo (object[] destination, int arrayIndex)
527                         {
528                                 list.CopyTo (destination, arrayIndex);
529                         }
530
531                         public IEnumerator GetEnumerator ()
532                         {
533                                 return list.GetEnumerator();
534                         }
535
536                         public int IndexOf (object value)
537                         {
538                                 return list.IndexOf(value);
539                         }
540
541                         public void Insert (int index, object item)
542                         {
543                                 ThrowIfOwnerIsDataBound ();
544                                 InsertInternal (index, item);
545                                 SyncOwnerItems ();
546                         }
547
548                         internal void InsertInternal (int index, object item)
549                         {
550                                 list.Insert (index, item);
551                         }
552
553                         public void Remove (object value)
554                         {
555                                 ThrowIfOwnerIsDataBound ();
556                                 RemoveInternal (value);
557                                 SyncOwnerItems ();
558                         }
559
560                         internal void RemoveInternal (object value)
561                         {
562                                 list.Remove (value);
563                         }
564
565                         public void RemoveAt (int index)
566                         {
567                                 ThrowIfOwnerIsDataBound ();
568                                 RemoveAtInternal (index);
569                                 SyncOwnerItems ();
570                         }
571
572                         internal void RemoveAtInternal (int index)
573                         {
574                                 list.RemoveAt (index);
575                         }
576
577                         int IList.Add (object item)
578                         {
579                                 return Add (item);
580                         }
581
582                 }
583
584         }
585
586 }
587