Fix bug #395
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / DataGridBoolColumn.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-2007 Novell, Inc. (http://www.novell.com)
21 //
22 // Author:
23 //      Jordi Mas i Hernandez <jordi@ximian.com>
24 //      Chris toshok <toshok@ximian.com>
25 //
26 //
27
28 using System.ComponentModel;
29 using System.Drawing;
30 using System.Runtime.InteropServices;
31 using System.Diagnostics;
32 using System.Collections;
33
34 namespace System.Windows.Forms
35 {
36         public class DataGridBoolColumn : DataGridColumnStyle
37         {
38                 [Flags]
39                 private enum CheckState {
40                         Checked         = 0x00000001,
41                         UnChecked       = 0x00000002,
42                         Null            = 0x00000004,
43                         Selected        = 0x00000008
44                 }
45
46                 #region Local Variables
47                 private bool allow_null;
48                 private object false_value;
49                 private object null_value;
50                 private object true_value;
51                 int editing_row;
52                 CheckState editing_state;
53                 CheckState model_state;
54                 Size checkbox_size;
55
56                 #endregion      // Local Variables
57
58                 #region Constructors
59                 public DataGridBoolColumn () : this (null, false)
60                 {
61                 }
62
63                 public DataGridBoolColumn (PropertyDescriptor prop) : this (prop, false)
64                 {
65                 }
66
67                 public DataGridBoolColumn (PropertyDescriptor prop, bool isDefault)  : base (prop)
68                 {
69                         false_value = false;
70                         null_value = null;
71                         true_value = true;
72                         allow_null = true;
73                         is_default = isDefault;
74                         checkbox_size = new Size (ThemeEngine.Current.DataGridMinimumColumnCheckBoxWidth, ThemeEngine.Current.DataGridMinimumColumnCheckBoxHeight);
75                 }
76                 #endregion
77
78                 #region Public Instance Properties
79                 [DefaultValue(true)]
80                 public bool AllowNull {
81                         get {
82                                 return allow_null;
83                         }
84                         set {
85                                 if (value != allow_null) {
86                                         allow_null = value;
87
88                                         EventHandler eh = (EventHandler)(Events [AllowNullChangedEvent]);
89                                         if (eh != null)
90                                                 eh (this, EventArgs.Empty);
91                                 }
92                         }
93                 }
94
95                 [TypeConverter(typeof(System.ComponentModel.StringConverter))]
96                 [DefaultValue (false)]
97                 public object FalseValue {
98                         get {
99                                 return false_value;
100                         }
101                         set {
102                                 if (value != false_value) {
103                                         false_value = value;
104
105                                         EventHandler eh = (EventHandler)(Events [FalseValueChangedEvent]);
106                                         if (eh != null)
107                                                 eh (this, EventArgs.Empty);
108                                 }
109                         }
110                 }
111
112                 [TypeConverter(typeof(System.ComponentModel.StringConverter))]
113                 public object NullValue {
114                         get {
115                                 return null_value;
116                         }
117                         set {
118                                 if (value != null_value) {
119                                         null_value = value;
120
121                                         // XXX no NullValueChangedEvent?  lame.
122                                 }
123                         }
124                 }
125
126                 [TypeConverter(typeof(System.ComponentModel.StringConverter))]
127                 [DefaultValue (true)]
128                 public object TrueValue {
129                         get {
130                                 return true_value;
131                         }
132                         set {
133                                 if (value != true_value) {
134                                         true_value = value;
135
136                                         EventHandler eh = (EventHandler)(Events [TrueValueChangedEvent]);
137                                         if (eh != null)
138                                                 eh (this, EventArgs.Empty);
139                                 }
140                         }
141                 }
142                 #endregion      // Public Instance Properties
143
144                 #region Public Instance Methods
145                 protected internal override void Abort (int rowNum)
146                 {
147                         if (rowNum == editing_row) {
148                                 // XXX 
149                                 // this needs to not use the current cell
150                                 // bounds, but the bounds of the cell for this
151                                 // column/rowNum.
152                                 grid.Invalidate (grid.GetCurrentCellBounds ());
153                                 editing_row = -1;
154                         }
155                 }
156
157                 protected internal override bool Commit (CurrencyManager dataSource, int rowNum)
158                 {
159                         if (rowNum == editing_row) {
160                                 SetColumnValueAtRow (dataSource, rowNum, FromStateToValue (editing_state));
161                                 // XXX 
162                                 // this needs to not use the current cell
163                                 // bounds, but the bounds of the cell for this
164                                 // column/rowNum.
165                                 grid.Invalidate (grid.GetCurrentCellBounds ());
166                                 editing_row = -1;
167                         }
168                         return true;
169                 }
170
171                 [MonoTODO ("Stub, does nothing")]
172                 protected internal override void ConcedeFocus ()
173                 {
174                         base.ConcedeFocus ();
175                 }
176
177                 protected internal override void Edit (CurrencyManager source, int rowNum, Rectangle bounds, bool readOnly, string displayText,  bool cellIsVisible)
178                 {
179                         editing_row = rowNum;
180                         model_state = FromValueToState (GetColumnValueAtRow (source, rowNum));
181                         editing_state = model_state | CheckState.Selected;
182                         // XXX 
183                         // this needs to not use the current cell
184                         // bounds, but the bounds of the cell for this
185                         // column/rowNum.
186                         grid.Invalidate (grid.GetCurrentCellBounds ());
187                 }
188
189                 [MonoTODO ("Stub, does nothing")]
190                 protected internal override void EnterNullValue ()
191                 {
192                         base.EnterNullValue ();
193                 }
194
195                 private bool ValueEquals (object value, object obj)
196                 {
197                         return value == null ? obj == null : value.Equals (obj);
198                 }
199
200                 protected internal override object GetColumnValueAtRow (CurrencyManager lm, int row)
201                 {
202                         object obj = base.GetColumnValueAtRow (lm, row);
203
204                         if (ValueEquals (DBNull.Value, obj))
205                                 return null_value;
206
207                         if (ValueEquals (true, obj))
208                                 return true_value;
209
210                         return false_value;
211                 }
212
213                 protected internal override int GetMinimumHeight ()
214                 {
215                         return checkbox_size.Height;
216                 }
217
218                 protected internal override int GetPreferredHeight (Graphics g, object value)
219                 {
220                         return checkbox_size.Height;
221                 }
222
223                 protected internal override Size GetPreferredSize (Graphics g, object value)
224                 {
225                         return checkbox_size;
226                 }
227
228                 protected internal override void Paint (Graphics g, Rectangle bounds, CurrencyManager source, int rowNum)
229                 {
230                         Paint (g, bounds, source, rowNum, false);
231                 }
232
233                 protected internal override void Paint (Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, bool alignToRight)
234                 {
235                         Paint (g, bounds, source, rowNum, ThemeEngine.Current.ResPool.GetSolidBrush (DataGridTableStyle.BackColor),
236                                 ThemeEngine.Current.ResPool.GetSolidBrush (DataGridTableStyle.ForeColor), alignToRight);
237                 }
238
239                 protected internal override void Paint (Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, Brush backBrush, Brush foreBrush, bool alignToRight)
240                 {
241                         Rectangle rect = new Rectangle ();                      
242                         ButtonState state;
243                         CheckState check_state;
244
245                         if (rowNum == editing_row)
246                                 check_state = editing_state;
247                         else
248                                 check_state = FromValueToState (GetColumnValueAtRow (source, rowNum));
249
250                         rect.X = bounds.X + ((bounds.Width - checkbox_size.Width - 2) / 2);
251                         rect.Y = bounds.Y + ((bounds.Height - checkbox_size.Height - 2) / 2);
252                         rect.Width = checkbox_size.Width - 2;
253                         rect.Height = checkbox_size.Height - 2;
254                         
255                         // If the cell is selected
256                         if ((check_state & CheckState.Selected) == CheckState.Selected) { 
257                                 backBrush = ThemeEngine.Current.ResPool.GetSolidBrush (grid.SelectionBackColor);
258                                 check_state &= ~CheckState.Selected;
259                         }
260                                                 
261                         g.FillRectangle (backBrush, bounds);                    
262                         
263                         switch (check_state) {
264                         case CheckState.Checked:
265                                 state = ButtonState.Checked;
266                                 break;
267                         case CheckState.Null:
268                                 state = ButtonState.Checked | ButtonState.Inactive;
269                                 break;
270                         case CheckState.UnChecked:
271                         default:
272                                 state = ButtonState.Normal;
273                                 break;
274                         }
275
276                         ThemeEngine.Current.CPDrawCheckBox (g, rect, state);
277                         PaintGridLine (g, bounds);
278                 }
279
280                 protected internal override void SetColumnValueAtRow (CurrencyManager lm, int row, object value)
281                 {
282                         object final_value = null;
283
284                         if (ValueEquals (null_value, value))
285                                 final_value = DBNull.Value;
286                         else if (ValueEquals (true_value, value))
287                                 final_value = true;
288                         else if (ValueEquals (false_value, value))
289                                 final_value = false;
290                         /* else error? */
291
292                         base.SetColumnValueAtRow (lm, row, final_value);
293                 }
294                 #endregion      // Public Instance Methods
295
296                 #region Private Instance Methods
297                 private object FromStateToValue (CheckState state)
298                 {
299                         if ((state & CheckState.Checked) == CheckState.Checked)
300                                 return true_value;
301                         else if ((state & CheckState.Null) == CheckState.Null)
302                                 return null_value;
303                         else
304                                 return false_value;
305                 }
306
307                 private CheckState FromValueToState (object obj)
308                 {
309                         if (ValueEquals (true_value, obj))
310                                 return CheckState.Checked;
311                         else if (ValueEquals (null_value, obj))
312                                 return CheckState.Null;
313                         else
314                                 return CheckState.UnChecked;
315                 }
316
317                 private CheckState GetNextState (CheckState state)
318                 {
319                         CheckState new_state;
320
321                         switch (state & ~CheckState.Selected) {
322                         case CheckState.Checked:
323                                 if (AllowNull)
324                                         new_state = CheckState.Null;
325                                 else
326                                         new_state = CheckState.UnChecked;
327                                 break;
328                         case CheckState.Null:
329                                 new_state = CheckState.UnChecked;
330                                 break;
331                         case CheckState.UnChecked:
332                         default:
333                                 new_state = CheckState.Checked;
334                                 break;
335                         }
336                         
337                         new_state |= (state & CheckState.Selected);
338
339                         return new_state;
340                 }
341
342                 internal override void OnKeyDown (KeyEventArgs ke, int row, int column)
343                 {
344                         switch (ke.KeyCode) {
345                         case Keys.Space:
346                                 NextState (row, column);
347                                 break;
348                         }
349                 }
350
351                 internal override void OnMouseDown (MouseEventArgs e, int row, int column)
352                 {
353                         NextState (row, column);
354                 }
355
356                 private void NextState (int row, int column)
357                 {
358                         grid.ColumnStartedEditing (new Rectangle());
359
360                         editing_state = GetNextState (editing_state);
361
362                         grid.Invalidate (grid.GetCellBounds (row, column));
363                 }
364
365                 #endregion Private Instance Methods
366
367                 #region Events
368                 static object AllowNullChangedEvent = new object ();
369                 static object FalseValueChangedEvent = new object ();
370                 static object TrueValueChangedEvent = new object ();
371
372                 public event EventHandler AllowNullChanged {
373                         add { Events.AddHandler (AllowNullChangedEvent, value); }
374                         remove { Events.RemoveHandler (AllowNullChangedEvent, value); }
375                 }
376
377                 public event EventHandler FalseValueChanged {
378                         add { Events.AddHandler (FalseValueChangedEvent, value); }
379                         remove { Events.RemoveHandler (FalseValueChangedEvent, value); }
380                 }
381
382                 public event EventHandler TrueValueChanged {
383                         add { Events.AddHandler (TrueValueChangedEvent, value); }
384                         remove { Events.RemoveHandler (TrueValueChangedEvent, value); }
385                 }
386                 #endregion      // Events
387         }
388 }