* TabControl.cs: Show the tooltip depending on the value
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / CurrencyManager.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.
21 //
22 // Authors:
23 //      Jackson Harper (jackson@ximian.com)
24 //      Ivan N. Zlatev (contact@i-nz.net)
25 //
26
27 using System;
28 using System.Data;
29 using System.Reflection;
30 using System.Collections;
31 using System.ComponentModel;
32
33 namespace System.Windows.Forms {
34         public class CurrencyManager : BindingManagerBase {
35
36                 protected int listposition;
37                 protected Type finalType;
38
39                 private IList list;
40                 private bool binding_suspended;
41
42                 private object data_source;
43
44                 bool editing;
45
46                 internal CurrencyManager ()
47                 {
48                 }
49
50                 internal CurrencyManager (object data_source)
51                 {
52                         SetDataSource (data_source);
53                 }
54
55                 public IList List {
56                         get { return list; }
57                 }
58
59                 public override object Current {
60                         get {
61                                 if (listposition == -1 || listposition >= list.Count) {
62                                         // Console.WriteLine ("throwing exception from here");
63                                         // Console.WriteLine (Environment.StackTrace);
64                                         throw new IndexOutOfRangeException ("list position");
65                                 }
66                                 return list [listposition];
67                         }
68                 }
69
70                 public override int Count {
71                         get { return list.Count; }
72                 }
73
74                 public override int Position {
75                         get {
76                                 return listposition;
77                         } 
78                         set {
79                                 if (value < 0)
80                                         value = 0;
81                                 if (value >= list.Count)
82                                         value = list.Count - 1;
83                                 if (listposition == value)
84                                         return;
85
86                                 if (listposition != -1)
87                                         EndCurrentEdit ();
88
89                                 listposition = value;
90                                 OnCurrentChanged (EventArgs.Empty);
91                                 OnPositionChanged (EventArgs.Empty);
92                         }
93                 }
94
95                 internal void SetDataSource (object data_source)
96                 {
97                         if (this.data_source is IBindingList)
98                                 ((IBindingList)this.data_source).ListChanged -= new ListChangedEventHandler (ListChangedHandler);
99
100                         if (data_source is IListSource)
101                                 data_source = ((IListSource)data_source).GetList();
102
103                         this.data_source = data_source;
104                         if (data_source != null)
105                                 this.finalType = data_source.GetType();
106
107                         listposition = -1;
108                         if (this.data_source is IBindingList)
109                                 ((IBindingList)this.data_source).ListChanged += new ListChangedEventHandler (ListChangedHandler);
110
111                         list = (IList)data_source;
112
113                         // XXX this is wrong.  MS invokes OnItemChanged directly, which seems to call PushData.
114                         ListChangedHandler (null, new ListChangedEventArgs (ListChangedType.Reset, -1));
115                 }
116
117                 public override PropertyDescriptorCollection GetItemProperties ()
118                 {
119                         return ListBindingHelper.GetListItemProperties (list);
120                 }
121
122                 public override void RemoveAt (int index)
123                 {
124                         list.RemoveAt (index);
125                 }
126
127                 public override void SuspendBinding ()
128                 {
129                         binding_suspended = true;
130                 }
131                 
132                 public override void ResumeBinding ()
133                 {
134                         binding_suspended = false;
135                 }
136
137                 internal override bool IsSuspended {
138                         get {
139                                 // Always return true if we don't have items
140                                 if (Count == 0)
141                                         return true;
142
143                                 return binding_suspended;
144                         }
145                 }
146
147                 internal bool AllowNew {
148                         get {
149                                 if (list is IBindingList)
150                                         return ((IBindingList)list).AllowNew;
151
152                                 if (list.IsReadOnly)
153                                         return false;
154
155                                 return false;
156                         }
157                 }
158
159                 internal bool AllowRemove {
160                         get {
161                                 if (list.IsReadOnly)
162                                         return false;
163
164                                 if (list is IBindingList)
165                                         return ((IBindingList)list).AllowRemove;
166
167                                 return false;
168                         }
169                 }
170
171                 internal bool AllowEdit {
172                         get {
173                                 if (list is IBindingList)
174                                         return ((IBindingList)list).AllowEdit;
175
176                                 return false;
177                         }
178                 }
179
180                 public override void AddNew ()
181                 {
182                         IBindingList ibl = list as IBindingList;
183
184                         if (ibl == null)
185                                 throw new NotSupportedException ();
186
187                         ibl.AddNew ();
188
189                         bool validate = (Position != (list.Count - 1));
190                         ChangeRecordState (list.Count - 1, validate, validate, true, true);
191                 }
192
193
194                 void BeginEdit ()
195                 {
196                         IEditableObject editable = Current as IEditableObject;
197
198                         if (editable != null) {
199                                 try {
200                                         editable.BeginEdit ();
201                                         editing = true;
202                                 }
203                                 catch {
204                                         /* swallow exceptions in IEditableObject.BeginEdit () */
205                                 }
206                         }
207                 }
208
209                 public override void CancelCurrentEdit ()
210                 {
211                         if (listposition == -1)
212                                 return;
213
214                         IEditableObject editable = Current as IEditableObject;
215
216                         if (editable != null) {
217                                 editing = false;
218                                 editable.CancelEdit ();
219                                 OnItemChanged (new ItemChangedEventArgs (Position));
220                         }
221 #if NET_2_0
222                         if (list is ICancelAddNew)
223                                 ((ICancelAddNew)list).CancelNew (listposition);
224 #endif
225
226                 }
227                 
228                 public override void EndCurrentEdit ()
229                 {
230                         if (listposition == -1)
231                                 return;
232
233                         IEditableObject editable = Current as IEditableObject;
234
235                         if (editable != null) {
236                                 editing = false;
237                                 editable.EndEdit ();
238                         }
239
240 #if NET_2_0
241                         if (list is ICancelAddNew)
242                                 ((ICancelAddNew)list).EndNew (listposition);
243 #endif
244                 }
245
246                 public void Refresh ()
247                 {
248                         ListChangedHandler (null, new ListChangedEventArgs (ListChangedType.Reset, -1));
249                 }
250
251                 protected void CheckEmpty ()
252                 {
253                         if (list == null || list.Count < 1)
254                                 throw new Exception ("List is empty.");
255                                 
256                 }
257
258                 protected internal override void OnCurrentChanged (EventArgs e)
259                 {
260                         if (onCurrentChangedHandler != null) {
261                                 onCurrentChangedHandler (this, e);
262                         }
263
264 #if NET_2_0
265                         // don't call OnCurrentItemChanged here, as it can be overridden
266                         if (onCurrentItemChangedHandler != null) {
267                                 onCurrentItemChangedHandler (this, e);
268                         }
269 #endif
270
271                 }
272
273 #if NET_2_0
274                 protected override void OnCurrentItemChanged (EventArgs e)
275                 {
276                         if (onCurrentItemChangedHandler != null) {
277                                 onCurrentItemChangedHandler (this, e);
278                         }
279                 }
280 #endif
281
282                 protected virtual void OnItemChanged (ItemChangedEventArgs e)
283                 {
284                         if (ItemChanged != null)
285                                 ItemChanged (this, e);
286
287                         transfering_data = true;
288                         PushData ();
289                         transfering_data = false;
290                 }
291
292 #if NET_2_0
293                 void OnListChanged (ListChangedEventArgs args)
294                 {
295                         if (ListChanged != null)
296                                 ListChanged (this, args);
297                 }
298 #endif
299
300                 protected virtual void OnPositionChanged (EventArgs e)
301                 {
302                         if (onPositionChangedHandler != null)
303                                 onPositionChangedHandler (this, e);
304                 }
305
306                 protected internal override string GetListName (ArrayList listAccessors)
307                 {
308                         if (list is ITypedList) {
309                                 PropertyDescriptor [] pds = null;
310                                 if (listAccessors != null) {
311                                         pds = new PropertyDescriptor [listAccessors.Count];
312                                         listAccessors.CopyTo (pds, 0);
313                                 }
314                                 return ((ITypedList) list).GetListName (pds);
315                         }
316                         else if (finalType != null) {
317                                 return finalType.Name;
318                         }
319                         return String.Empty;
320                 }
321
322                 protected override void UpdateIsBinding ()
323                 {
324                         UpdateItem ();
325
326                         foreach (Binding binding in Bindings)
327                                 binding.UpdateIsBinding ();
328
329                         ChangeRecordState (listposition, false, false, true, false);
330
331                         OnItemChanged (new ItemChangedEventArgs (-1));
332                 }
333
334                 private void ChangeRecordState (int newPosition,
335                                                 bool validating,
336                                                 bool endCurrentEdit,
337                                                 bool firePositionChanged,
338                                                 bool pullData)
339                 {
340                         if (endCurrentEdit)
341                                 EndCurrentEdit ();
342
343                         int old_index = listposition;
344
345                         listposition = newPosition;
346
347                         if (listposition >= list.Count)
348                                 listposition = list.Count - 1;
349
350                         if (old_index != -1 && listposition != -1)
351                                 OnCurrentChanged (EventArgs.Empty);
352
353                         if (firePositionChanged)
354                                 OnPositionChanged (EventArgs.Empty);
355                 }
356
357                 private void UpdateItem ()
358                 {
359                         // Probably should be validating or something here
360                         if (!transfering_data && listposition == -1 && list.Count > 0) {
361                                 listposition = 0;
362                                 BeginEdit ();
363                         }
364                 }
365                 
366                 internal object this [int index] {
367                         get { return list [index]; }
368                 }               
369                 
370                 private PropertyDescriptorCollection GetBrowsableProperties (Type t)
371                 {
372                         Attribute [] att = new System.Attribute [1];
373                         att [0] = new BrowsableAttribute (true);
374                         return TypeDescriptor.GetProperties (t, att);
375                 }
376
377 #if NET_2_0
378                 protected
379 #else
380                 private
381 #endif
382                 void OnMetaDataChanged (EventArgs e)
383                 {
384                         if (MetaDataChanged != null)
385                                 MetaDataChanged (this, e);
386                 }
387
388                 private void ListChangedHandler (object sender, ListChangedEventArgs e)
389                 {
390                         switch (e.ListChangedType) {
391                         case ListChangedType.PropertyDescriptorAdded:
392                         case ListChangedType.PropertyDescriptorDeleted:
393                         case ListChangedType.PropertyDescriptorChanged:
394                                 OnMetaDataChanged (EventArgs.Empty);
395 #if NET_2_0
396                                 OnListChanged (e);
397 #endif
398                                 break;
399                         case ListChangedType.ItemDeleted:
400                                 if (list.Count == 0) {
401                                         /* the last row was deleted */
402                                         listposition = -1;
403                                         UpdateIsBinding ();
404
405 #if NET_2_0
406                                         OnPositionChanged (EventArgs.Empty);
407                                         OnCurrentChanged (EventArgs.Empty);
408 #endif
409                                 }
410                                 else if (e.NewIndex <= listposition) {
411                                         /* the deleted row was either the current one, or one earlier in the list.
412                                            Update the index and emit PositionChanged, CurrentChanged, and ItemChanged. */
413                                         ChangeRecordState (e.NewIndex,
414                                                            false, false, e.NewIndex != listposition, false);
415                                 }
416                                 else {
417                                         /* the deleted row was after the current one, so we don't
418                                            need to update bound controls for Position/Current changed */
419                                 }
420
421                                 OnItemChanged (new ItemChangedEventArgs (-1));
422 #if NET_2_0
423                                 OnListChanged (e);
424 #endif
425                                 break;
426                         case ListChangedType.ItemAdded:
427                                 if (list.Count == 1) {
428                                         /* it's the first one we've added */
429                                         ChangeRecordState (e.NewIndex,
430                                                            false, false, true, false);
431
432 #if ONLY_1_1
433                                         UpdateIsBinding ();
434 #else
435                                         OnItemChanged (new ItemChangedEventArgs (-1));
436                                         OnListChanged (e);
437 #endif                                          
438                                 }
439                                 else {
440 #if NET_2_0
441                                         if (e.NewIndex <= listposition) {
442                                                 ChangeRecordState (listposition + 1,
443                                                                    false, false, false, false);
444                                                 OnItemChanged (new ItemChangedEventArgs (-1));
445                                                 OnListChanged (e);
446                                                 OnPositionChanged (EventArgs.Empty);
447                                         }
448                                         else {
449                                                 OnItemChanged (new ItemChangedEventArgs (-1));
450                                                 OnListChanged (e);
451                                         }
452 #else
453                                         OnItemChanged (new ItemChangedEventArgs (-1));
454 #endif
455                                 }
456
457                                 break;
458                         case ListChangedType.ItemChanged:
459                                 if (editing) {
460 #if NET_2_0
461                                         if (e.NewIndex == listposition)
462                                                 OnCurrentItemChanged (EventArgs.Empty);
463 #endif
464                                         OnItemChanged (new ItemChangedEventArgs (e.NewIndex));
465                                 }
466 #if NET_2_0
467                                 OnListChanged (e);
468 #endif                                          
469                                 break;
470                         case ListChangedType.Reset:
471                                 PushData();
472                                 UpdateIsBinding();
473 #if NET_2_0     
474                                 OnListChanged (e);
475 #endif
476                                 break;
477                         default:
478 #if NET_2_0
479                                 OnListChanged (e);
480 #endif
481                                 break;
482                         }
483                 }
484
485 #if NET_2_0
486                 public event ListChangedEventHandler ListChanged;
487 #endif
488                 public event ItemChangedEventHandler ItemChanged;
489                 public event EventHandler MetaDataChanged;
490         }
491 }
492