In System.Windows.Forms:
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / CurrencyManager.cs
blobb0fb3e7a3fc199e2b4b4c8c11079e987bcb223d9
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.
20 // Copyright (c) 2005 Novell, Inc.
22 // Authors:
23 // Jackson Harper (jackson@ximian.com)
26 using System;
27 using System.Data;
28 using System.Reflection;
29 using System.Collections;
30 using System.ComponentModel;
32 namespace System.Windows.Forms {
33 public class CurrencyManager : BindingManagerBase {
35 protected int listposition;
36 protected Type finalType;
38 private IList list;
39 private bool binding_suspended;
41 private object data_source;
43 bool editing;
45 internal CurrencyManager ()
49 internal CurrencyManager (object data_source)
51 SetDataSource (data_source);
54 public IList List {
55 get { return list; }
58 public override object Current {
59 get {
60 if (listposition == -1 || listposition >= list.Count) {
61 // Console.WriteLine ("throwing exception from here");
62 // Console.WriteLine (Environment.StackTrace);
63 throw new IndexOutOfRangeException ("list position");
65 return list [listposition];
69 public override int Count {
70 get { return list.Count; }
73 public override int Position {
74 get {
75 return listposition;
77 set {
78 if (value < 0)
79 value = 0;
80 if (value == list.Count)
81 value = list.Count - 1;
82 if (listposition == value)
83 return;
85 if (listposition != -1)
86 EndCurrentEdit ();
88 listposition = value;
89 OnCurrentChanged (EventArgs.Empty);
90 OnPositionChanged (EventArgs.Empty);
94 internal void SetDataSource (object data_source)
96 if (this.data_source is IBindingList)
97 ((IBindingList)this.data_source).ListChanged -= new ListChangedEventHandler (ListChangedHandler);
99 if (data_source is IListSource)
100 data_source = ((IListSource)data_source).GetList();
102 this.data_source = data_source;
103 if (data_source != null)
104 this.finalType = data_source.GetType();
106 listposition = -1;
107 if (this.data_source is IBindingList)
108 ((IBindingList)this.data_source).ListChanged += new ListChangedEventHandler (ListChangedHandler);
110 list = (IList)data_source;
112 // XXX this is wrong. MS invokes OnItemChanged directly, which seems to call PushData.
113 ListChangedHandler (null, new ListChangedEventArgs (ListChangedType.Reset, -1));
116 public override PropertyDescriptorCollection GetItemProperties ()
118 if (list is Array) {
119 Type element = list.GetType ().GetElementType ();
120 return TypeDescriptor.GetProperties (element);
123 if (list is ITypedList) {
124 return ((ITypedList)list).GetItemProperties (null);
127 PropertyInfo [] props = data_source.GetType().GetProperties ();
128 for (int i = 0; i < props.Length; i++) {
129 if (props [i].Name == "Item") {
130 Type t = props [i].PropertyType;
131 if (t == typeof (object))
132 continue;
133 return GetBrowsableProperties (t);
137 if (list.Count > 0) {
138 return GetBrowsableProperties (list [0].GetType ());
141 return new PropertyDescriptorCollection (null);
144 public override void RemoveAt (int index)
146 list.RemoveAt (index);
149 public override void SuspendBinding ()
151 binding_suspended = true;
154 public override void ResumeBinding ()
156 binding_suspended = false;
159 internal override bool IsSuspended {
160 get {
161 // Always return true if we don't have items
162 if (Count == 0)
163 return true;
165 return binding_suspended;
169 // XXX this needs re-addressing once DataViewManager.AllowNew is implemented
170 internal bool CanAddRows {
171 get {
172 /* if we're readonly, don't even bother checking if we can add new rows */
173 if (list.IsReadOnly)
174 return false;
176 if (list is IBindingList) {
177 return true;
178 //return ((IBindingList)list).AllowNew;
181 return false;
185 public override void AddNew ()
187 IBindingList ibl = list as IBindingList;
189 if (ibl == null)
190 throw new NotSupportedException ();
192 ibl.AddNew ();
194 ChangeRecordState (list.Count - 1, true, true, true, true);
198 void BeginEdit ()
200 IEditableObject editable = Current as IEditableObject;
202 if (editable != null) {
203 try {
204 editable.BeginEdit ();
205 editing = true;
207 catch {
208 /* swallow exceptions in IEditableObject.BeginEdit () */
213 public override void CancelCurrentEdit ()
215 if (listposition == -1)
216 return;
218 IEditableObject editable = Current as IEditableObject;
220 if (editable != null) {
221 editing = false;
222 editable.CancelEdit ();
223 OnItemChanged (new ItemChangedEventArgs (Position));
225 #if NET_2_0
226 if (list is ICancelAddNew)
227 ((ICancelAddNew)list).CancelNew (listposition);
228 #endif
232 public override void EndCurrentEdit ()
234 if (listposition == -1)
235 return;
237 IEditableObject editable = Current as IEditableObject;
239 if (editable != null) {
240 editing = false;
241 editable.EndEdit ();
244 #if NET_2_0
245 if (list is ICancelAddNew)
246 ((ICancelAddNew)list).EndNew (listposition);
247 #endif
250 public void Refresh ()
252 ListChangedHandler (null, new ListChangedEventArgs (ListChangedType.Reset, -1));
255 protected void CheckEmpty ()
257 if (list == null || list.Count < 1)
258 throw new Exception ("List is empty.");
262 protected internal override void OnCurrentChanged (EventArgs e)
264 if (onCurrentChangedHandler != null) {
265 onCurrentChangedHandler (this, e);
268 #if NET_2_0
269 // don't call OnCurrentItemChanged here, as it can be overridden
270 if (onCurrentItemChangedHandler != null) {
271 onCurrentItemChangedHandler (this, e);
273 #endif
277 #if NET_2_0
278 protected override void OnCurrentItemChanged (EventArgs e)
280 if (onCurrentItemChangedHandler != null) {
281 onCurrentItemChangedHandler (this, e);
284 #endif
286 protected virtual void OnItemChanged (ItemChangedEventArgs e)
288 if (ItemChanged != null)
289 ItemChanged (this, e);
291 transfering_data = true;
292 PushData ();
293 transfering_data = false;
296 #if NET_2_0
297 void OnListChanged (ListChangedEventArgs args)
299 if (ListChanged != null)
300 ListChanged (this, args);
302 #endif
304 protected virtual void OnPositionChanged (EventArgs e)
306 if (onPositionChangedHandler != null)
307 onPositionChangedHandler (this, e);
310 protected internal override string GetListName (ArrayList listAccessors)
312 if (list is ITypedList) {
313 PropertyDescriptor [] pds = null;
314 if (listAccessors != null) {
315 pds = new PropertyDescriptor [listAccessors.Count];
316 listAccessors.CopyTo (pds, 0);
318 return ((ITypedList) list).GetListName (pds);
320 else if (finalType != null) {
321 return finalType.Name;
323 return String.Empty;
326 protected override void UpdateIsBinding ()
328 UpdateItem ();
330 foreach (Binding binding in Bindings)
331 binding.UpdateIsBinding ();
333 ChangeRecordState (listposition, false, false, true, false);
335 OnItemChanged (new ItemChangedEventArgs (-1));
338 private void ChangeRecordState (int newPosition,
339 bool validating,
340 bool endCurrentEdit,
341 bool firePositionChanged,
342 bool pullData)
344 if (endCurrentEdit)
345 EndCurrentEdit ();
347 int old_index = listposition;
349 listposition = newPosition;
351 if (listposition >= list.Count)
352 listposition = list.Count - 1;
354 if (old_index != -1 && listposition != -1)
355 OnCurrentChanged (EventArgs.Empty);
357 if (firePositionChanged)
358 OnPositionChanged (EventArgs.Empty);
361 private void UpdateItem ()
363 // Probably should be validating or something here
364 if (listposition != -1) {
366 else if (list.Count > 0) {
368 listposition = 0;
370 BeginEdit ();
374 internal object this [int index] {
375 get { return list [index]; }
378 private PropertyDescriptorCollection GetBrowsableProperties (Type t)
380 Attribute [] att = new System.Attribute [1];
381 att [0] = new BrowsableAttribute (true);
382 return TypeDescriptor.GetProperties (t, att);
385 #if NET_2_0
386 protected
387 #else
388 private
389 #endif
390 void OnMetaDataChanged (EventArgs e)
392 if (MetaDataChanged != null)
393 MetaDataChanged (this, e);
396 private void ListChangedHandler (object sender, ListChangedEventArgs e)
398 switch (e.ListChangedType) {
399 case ListChangedType.PropertyDescriptorAdded:
400 OnMetaDataChanged (EventArgs.Empty);
401 break;
402 case ListChangedType.PropertyDescriptorDeleted:
403 case ListChangedType.PropertyDescriptorChanged:
404 OnMetaDataChanged (EventArgs.Empty);
405 break;
406 case ListChangedType.ItemDeleted:
407 if (list.Count == 0) {
408 /* the last row was deleted */
409 listposition = -1;
410 UpdateIsBinding ();
412 #if NET_2_0
413 OnPositionChanged (EventArgs.Empty);
414 OnCurrentChanged (EventArgs.Empty);
415 #endif
417 else if (e.NewIndex <= listposition) {
418 /* the deleted row was either the current one, or one earlier in the list.
419 Update the index and emit PositionChanged, CurrentChanged, and ItemChanged. */
420 ChangeRecordState (e.NewIndex,
421 false, false, e.NewIndex != listposition, false);
423 else {
424 /* the deleted row was after the current one, so we don't
425 need to update bound controls for Position/Current changed */
428 OnItemChanged (new ItemChangedEventArgs (-1));
429 break;
430 case ListChangedType.ItemAdded:
431 if (list.Count == 1) {
432 /* it's the first one we've added */
433 ChangeRecordState (e.NewIndex,
434 false, false, true, false);
436 #if ONLY_1_1
437 UpdateIsBinding ();
438 #else
439 OnItemChanged (new ItemChangedEventArgs (-1));
440 #endif
442 else {
443 #if NET_2_0
444 if (e.NewIndex <= listposition) {
445 ChangeRecordState (listposition + 1,
446 false, false, false, false);
447 OnItemChanged (new ItemChangedEventArgs (-1));
448 OnListChanged (e);
449 OnPositionChanged (EventArgs.Empty);
451 else {
452 OnItemChanged (new ItemChangedEventArgs (-1));
453 OnListChanged (e);
455 #else
456 OnItemChanged (new ItemChangedEventArgs (-1));
457 #endif
460 break;
461 case ListChangedType.ItemChanged:
462 if (editing) {
463 #if NET_2_0
464 if (e.NewIndex == listposition)
465 OnCurrentItemChanged (EventArgs.Empty);
466 #endif
467 OnItemChanged (new ItemChangedEventArgs (e.NewIndex));
469 break;
470 default:
471 PushData ();
472 OnItemChanged (new ItemChangedEventArgs (-1));
473 // UpdateIsBinding ();
474 break;
476 #if NET_2_0
477 OnListChanged (e);
478 #endif
481 #if NET_2_0
482 public event ListChangedEventHandler ListChanged;
483 #endif
484 public event ItemChangedEventHandler ItemChanged;
485 public event EventHandler MetaDataChanged;