2007-04-27 Chris Toshok <toshok@ximian.com>
[mcs.git] / class / Managed.Windows.Forms / System.Windows.Forms / CurrencyManager.cs
blobcc013632116339b6463117223e730ff6dd48a51d
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 { return binding_suspended; }
163 // XXX this needs re-addressing once DataViewManager.AllowNew is implemented
164 internal bool CanAddRows {
165 get {
166 /* if we're readonly, don't even bother checking if we can add new rows */
167 if (list.IsReadOnly)
168 return false;
170 if (list is IBindingList) {
171 return true;
172 //return ((IBindingList)list).AllowNew;
175 return false;
179 public override void AddNew ()
181 IBindingList ibl = list as IBindingList;
183 if (ibl == null)
184 throw new NotSupportedException ();
186 ibl.AddNew ();
188 ChangeRecordState (list.Count - 1, true, true, true, true);
192 void BeginEdit ()
194 IEditableObject editable = Current as IEditableObject;
196 if (editable != null) {
197 try {
198 editable.BeginEdit ();
199 editing = true;
201 catch {
202 /* swallow exceptions in IEditableObject.BeginEdit () */
207 public override void CancelCurrentEdit ()
209 if (listposition == -1)
210 return;
212 IEditableObject editable = Current as IEditableObject;
214 if (editable != null) {
215 editing = false;
216 editable.CancelEdit ();
217 OnItemChanged (new ItemChangedEventArgs (Position));
221 public override void EndCurrentEdit ()
223 if (listposition == -1)
224 return;
226 IEditableObject editable = Current as IEditableObject;
228 if (editable != null) {
229 editing = false;
230 editable.EndEdit ();
234 public void Refresh ()
236 ListChangedHandler (null, new ListChangedEventArgs (ListChangedType.Reset, -1));
239 protected void CheckEmpty ()
241 if (list == null || list.Count < 1)
242 throw new Exception ("List is empty.");
246 protected internal override void OnCurrentChanged (EventArgs e)
248 if (onCurrentChangedHandler != null) {
249 onCurrentChangedHandler (this, e);
252 #if NET_2_0
253 // don't call OnCurrentItemChanged here, as it can be overridden
254 if (onCurrentItemChangedHandler != null) {
255 onCurrentItemChangedHandler (this, e);
257 #endif
261 #if NET_2_0
262 protected override void OnCurrentItemChanged (EventArgs e)
264 if (onCurrentItemChangedHandler != null) {
265 onCurrentItemChangedHandler (this, e);
268 #endif
270 protected virtual void OnItemChanged (ItemChangedEventArgs e)
272 if (ItemChanged != null)
273 ItemChanged (this, e);
275 #if fales
276 // XXX see the commend in BindingManagerbase.PushData
277 if (listposition != -1)
278 PushData ();
279 #endif
282 protected virtual void OnPositionChanged (EventArgs e)
284 if (onPositionChangedHandler != null)
285 onPositionChangedHandler (this, e);
288 protected internal override string GetListName (ArrayList accessors)
290 if (list is ITypedList) {
291 PropertyDescriptor [] pds = null;
292 if (accessors != null) {
293 pds = new PropertyDescriptor [accessors.Count];
294 accessors.CopyTo (pds, 0);
296 return ((ITypedList) list).GetListName (pds);
298 else if (finalType != null) {
299 return finalType.Name;
301 return String.Empty;
304 protected override void UpdateIsBinding ()
306 UpdateItem ();
308 foreach (Binding binding in Bindings)
309 binding.UpdateIsBinding ();
311 ChangeRecordState (listposition, false, false, true, false);
313 OnItemChanged (new ItemChangedEventArgs (-1));
316 private void ChangeRecordState (int newPosition,
317 bool validating,
318 bool endCurrentEdit,
319 bool firePositionChanged,
320 bool pullData)
322 if (endCurrentEdit)
323 EndCurrentEdit ();
325 int old_index = listposition;
327 listposition = newPosition;
329 if (listposition >= list.Count)
330 listposition = list.Count - 1;
332 if (listposition != -1)
333 OnCurrentChanged (EventArgs.Empty);
335 if (firePositionChanged)
336 OnPositionChanged (EventArgs.Empty);
339 private void UpdateItem ()
341 // Probably should be validating or something here
342 if (listposition != -1) {
344 else if (list.Count > 0) {
346 listposition = 0;
348 BeginEdit ();
352 internal object this [int index] {
353 get { return list [index]; }
356 private PropertyDescriptorCollection GetBrowsableProperties (Type t)
358 Attribute [] att = new System.Attribute [1];
359 att [0] = new BrowsableAttribute (true);
360 return TypeDescriptor.GetProperties (t, att);
363 #if NET_2_0
364 protected
365 #else
366 private
367 #endif
368 void OnMetaDataChanged (EventArgs args)
370 if (MetaDataChanged != null)
371 MetaDataChanged (this, args);
374 private void ListChangedHandler (object sender, ListChangedEventArgs e)
376 switch (e.ListChangedType) {
377 case ListChangedType.PropertyDescriptorAdded:
378 OnMetaDataChanged (EventArgs.Empty);
379 #if ONLY_1_1
380 // um...
381 OnMetaDataChanged (EventArgs.Empty);
382 #endif
383 break;
384 case ListChangedType.PropertyDescriptorDeleted:
385 case ListChangedType.PropertyDescriptorChanged:
386 OnMetaDataChanged (EventArgs.Empty);
387 break;
388 case ListChangedType.ItemDeleted:
389 if (list.Count == 0) {
390 /* the last row was deleted */
391 listposition = -1;
392 UpdateIsBinding ();
394 #if NET_2_0
395 OnPositionChanged (EventArgs.Empty);
396 OnCurrentChanged (EventArgs.Empty);
397 #endif
399 else if (e.NewIndex <= listposition) {
400 /* the deleted row was either the current one, or one earlier in the list.
401 Update the index and emit PositionChanged, CurrentChanged, and ItemChanged. */
402 ChangeRecordState (e.NewIndex,
403 false, false, e.NewIndex != listposition, false);
405 else {
406 /* the deleted row was after the current one, so we don't
407 need to update bound controls for Position/Current changed */
410 OnItemChanged (new ItemChangedEventArgs (-1));
411 break;
412 case ListChangedType.ItemAdded:
413 if (list.Count == 1) {
414 /* it's the first one we've added */
415 ChangeRecordState (e.NewIndex,
416 false, false, true, false);
418 OnItemChanged (new ItemChangedEventArgs (-1));
420 else {
421 #if NET_2_0
422 if (e.NewIndex <= listposition) {
423 ChangeRecordState (listposition,
424 false, false, false, false);
425 OnItemChanged (new ItemChangedEventArgs (-1));
426 OnPositionChanged (EventArgs.Empty);
428 else {
429 OnItemChanged (new ItemChangedEventArgs (-1));
431 #else
432 OnItemChanged (new ItemChangedEventArgs (-1));
433 #endif
436 break;
437 case ListChangedType.ItemChanged:
438 if (editing) {
439 #if NET_2_0
440 if (e.NewIndex == listposition)
441 OnCurrentItemChanged (EventArgs.Empty);
442 #endif
443 OnItemChanged (new ItemChangedEventArgs (e.NewIndex));
445 break;
446 default:
447 PushData ();
448 OnItemChanged (new ItemChangedEventArgs (-1));
449 // UpdateIsBinding ();
450 break;
454 #if NET_2_0
455 public event ListChangedEventHandler ListChanged;
456 #endif
457 public event ItemChangedEventHandler ItemChanged;
458 public event EventHandler MetaDataChanged;