diff options
Diffstat (limited to 'source/ShiftUI/Internal/CurrencyManager.cs')
| -rw-r--r-- | source/ShiftUI/Internal/CurrencyManager.cs | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/source/ShiftUI/Internal/CurrencyManager.cs b/source/ShiftUI/Internal/CurrencyManager.cs new file mode 100644 index 0000000..9f68fd4 --- /dev/null +++ b/source/ShiftUI/Internal/CurrencyManager.cs @@ -0,0 +1,453 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2005 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) +// Ivan N. Zlatev ([email protected]) +// + +using System; +using System.Data; +using System.Reflection; +using System.Collections; +using System.ComponentModel; + +namespace ShiftUI { + public class CurrencyManager : BindingManagerBase { + + protected int listposition; + protected Type finalType; + + private IList list; + private bool binding_suspended; + + private object data_source; + + bool editing; + + internal CurrencyManager () + { + } + + internal CurrencyManager (object data_source) + { + SetDataSource (data_source); + } + + public IList List { + get { return list; } + } + + public override object Current { + get { + if (listposition == -1 || listposition >= list.Count) { + // Console.WriteLine ("throwing exception from here"); + // Console.WriteLine (Environment.StackTrace); + throw new IndexOutOfRangeException ("list position"); + } + return list [listposition]; + } + } + + public override int Count { + get { return list.Count; } + } + + public override int Position { + get { + return listposition; + } + set { + if (value < 0) + value = 0; + if (value >= list.Count) + value = list.Count - 1; + if (listposition == value) + return; + + if (listposition != -1) + EndCurrentEdit (); + + listposition = value; + OnCurrentChanged (EventArgs.Empty); + OnPositionChanged (EventArgs.Empty); + } + } + + internal void SetDataSource (object data_source) + { + if (this.data_source is IBindingList) + ((IBindingList)this.data_source).ListChanged -= new ListChangedEventHandler (ListChangedHandler); + + if (data_source is IListSource) + data_source = ((IListSource)data_source).GetList(); + + this.data_source = data_source; + if (data_source != null) + this.finalType = data_source.GetType(); + + listposition = -1; + if (this.data_source is IBindingList) + ((IBindingList)this.data_source).ListChanged += new ListChangedEventHandler (ListChangedHandler); + + list = (IList)data_source; + + // XXX this is wrong. MS invokes OnItemChanged directly, which seems to call PushData. + ListChangedHandler (null, new ListChangedEventArgs (ListChangedType.Reset, -1)); + } + + public override PropertyDescriptorCollection GetItemProperties () + { + return ListBindingHelper.GetListItemProperties (list); + } + + public override void RemoveAt (int index) + { + list.RemoveAt (index); + } + + public override void SuspendBinding () + { + binding_suspended = true; + } + + public override void ResumeBinding () + { + binding_suspended = false; + } + + internal override bool IsSuspended { + get { + // Always return true if we don't have items + if (Count == 0) + return true; + + return binding_suspended; + } + } + + internal bool AllowNew { + get { + if (list is IBindingList) + return ((IBindingList)list).AllowNew; + + if (list.IsReadOnly) + return false; + + return false; + } + } + + internal bool AllowRemove { + get { + if (list.IsReadOnly) + return false; + + if (list is IBindingList) + return ((IBindingList)list).AllowRemove; + + return false; + } + } + + internal bool AllowEdit { + get { + if (list is IBindingList) + return ((IBindingList)list).AllowEdit; + + return false; + } + } + + public override void AddNew () + { + IBindingList ibl = list as IBindingList; + + if (ibl == null) + throw new NotSupportedException (); + + ibl.AddNew (); + + bool validate = (Position != (list.Count - 1)); + ChangeRecordState (list.Count - 1, validate, validate, true, true); + } + + + void BeginEdit () + { + IEditableObject editable = Current as IEditableObject; + + if (editable != null) { + try { + editable.BeginEdit (); + editing = true; + } + catch { + /* swallow exceptions in IEditableObject.BeginEdit () */ + } + } + } + + public override void CancelCurrentEdit () + { + if (listposition == -1) + return; + + IEditableObject editable = Current as IEditableObject; + + if (editable != null) { + editing = false; + editable.CancelEdit (); + OnItemChanged (new ItemChangedEventArgs (Position)); + } + if (list is ICancelAddNew) + ((ICancelAddNew)list).CancelNew (listposition); + } + + public override void EndCurrentEdit () + { + if (listposition == -1) + return; + + IEditableObject editable = Current as IEditableObject; + + if (editable != null) { + editing = false; + editable.EndEdit (); + } + + if (list is ICancelAddNew) + ((ICancelAddNew)list).EndNew (listposition); + } + + public void Refresh () + { + ListChangedHandler (null, new ListChangedEventArgs (ListChangedType.Reset, -1)); + } + + protected void CheckEmpty () + { + if (list == null || list.Count < 1) + throw new Exception ("List is empty."); + + } + + protected internal override void OnCurrentChanged (EventArgs e) + { + if (onCurrentChangedHandler != null) { + onCurrentChangedHandler (this, e); + } + + // don't call OnCurrentItemChanged here, as it can be overridden + if (onCurrentItemChangedHandler != null) { + onCurrentItemChangedHandler (this, e); + } + + } + + protected override void OnCurrentItemChanged (EventArgs e) + { + if (onCurrentItemChangedHandler != null) { + onCurrentItemChangedHandler (this, e); + } + } + + protected virtual void OnItemChanged (ItemChangedEventArgs e) + { + if (ItemChanged != null) + ItemChanged (this, e); + + transfering_data = true; + PushData (); + transfering_data = false; + } + + void OnListChanged (ListChangedEventArgs args) + { + if (ListChanged != null) + ListChanged (this, args); + } + + protected virtual void OnPositionChanged (EventArgs e) + { + if (onPositionChangedHandler != null) + onPositionChangedHandler (this, e); + } + + protected internal override string GetListName (ArrayList listAccessors) + { + if (list is ITypedList) { + PropertyDescriptor [] pds = null; + if (listAccessors != null) { + pds = new PropertyDescriptor [listAccessors.Count]; + listAccessors.CopyTo (pds, 0); + } + return ((ITypedList) list).GetListName (pds); + } + else if (finalType != null) { + return finalType.Name; + } + return String.Empty; + } + + protected override void UpdateIsBinding () + { + UpdateItem (); + + foreach (Binding binding in Bindings) + binding.UpdateIsBinding (); + + ChangeRecordState (listposition, false, false, true, false); + + OnItemChanged (new ItemChangedEventArgs (-1)); + } + + private void ChangeRecordState (int newPosition, + bool validating, + bool endCurrentEdit, + bool firePositionChanged, + bool pullData) + { + if (endCurrentEdit) + EndCurrentEdit (); + + int old_index = listposition; + + listposition = newPosition; + + if (listposition >= list.Count) + listposition = list.Count - 1; + + if (old_index != -1 && listposition != -1) + OnCurrentChanged (EventArgs.Empty); + + if (firePositionChanged) + OnPositionChanged (EventArgs.Empty); + } + + private void UpdateItem () + { + // Probably should be validating or something here + if (!transfering_data && listposition == -1 && list.Count > 0) { + listposition = 0; + BeginEdit (); + } + } + + internal object this [int index] { + get { return list [index]; } + } + + private PropertyDescriptorCollection GetBrowsableProperties (Type t) + { + Attribute [] att = new System.Attribute [1]; + att [0] = new BrowsableAttribute (true); + return TypeDescriptor.GetProperties (t, att); + } + + protected void OnMetaDataChanged (EventArgs e) + { + if (MetaDataChanged != null) + MetaDataChanged (this, e); + } + + private void ListChangedHandler (object sender, ListChangedEventArgs e) + { + switch (e.ListChangedType) { + case ListChangedType.PropertyDescriptorAdded: + case ListChangedType.PropertyDescriptorDeleted: + case ListChangedType.PropertyDescriptorChanged: + OnMetaDataChanged (EventArgs.Empty); + OnListChanged (e); + break; + case ListChangedType.ItemDeleted: + if (list.Count == 0) { + /* the last row was deleted */ + listposition = -1; + UpdateIsBinding (); + + OnPositionChanged (EventArgs.Empty); + OnCurrentChanged (EventArgs.Empty); + } + else if (e.NewIndex <= listposition) { + /* the deleted row was either the current one, or one earlier in the list. + Update the index and emit PositionChanged, CurrentChanged, and ItemChanged. */ + // FIXME: this looks wrong, shouldn't it be (listposition - 1) instead of e.NewIndex ? + ChangeRecordState (e.NewIndex, + false, false, e.NewIndex != listposition, false); + } + else { + /* the deleted row was after the current one, so we don't + need to update bound Widgets for Position/Current changed */ + } + + OnItemChanged (new ItemChangedEventArgs (-1)); + OnListChanged (e); + break; + case ListChangedType.ItemAdded: + if (list.Count == 1) { + /* it's the first one we've added */ + ChangeRecordState (e.NewIndex, + false, false, true, false); + + OnItemChanged (new ItemChangedEventArgs (-1)); + OnListChanged (e); + } + else { + if (e.NewIndex <= listposition) { + ChangeRecordState (listposition + 1, + false, false, false, false); + OnItemChanged (new ItemChangedEventArgs (-1)); + OnListChanged (e); + OnPositionChanged (EventArgs.Empty); + } + else { + OnItemChanged (new ItemChangedEventArgs (-1)); + OnListChanged (e); + } + } + + break; + case ListChangedType.ItemChanged: + if (editing) { + if (e.NewIndex == listposition) + OnCurrentItemChanged (EventArgs.Empty); + OnItemChanged (new ItemChangedEventArgs (e.NewIndex)); + } + OnListChanged (e); + break; + case ListChangedType.Reset: + PushData(); + UpdateIsBinding(); + OnListChanged (e); + break; + default: + OnListChanged (e); + break; + } + } + + public event ListChangedEventHandler ListChanged; + public event ItemChangedEventHandler ItemChanged; + public event EventHandler MetaDataChanged; + } +} + |
