2014-08-13 10:39:27 +01:00
|
|
|
// 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 (jackson@ximian.com)
|
|
|
|
// Ivan N. Zlatev (contact@i-nz.net)
|
|
|
|
//
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Data;
|
|
|
|
using System.Reflection;
|
|
|
|
using System.Collections;
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
|
|
|
namespace System.Windows.Forms {
|
|
|
|
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. */
|
2014-09-04 09:07:35 +01:00
|
|
|
// FIXME: this looks wrong, shouldn't it be (listposition - 1) instead of e.NewIndex ?
|
|
|
|
ChangeRecordState (e.NewIndex,
|
2014-08-13 10:39:27 +01:00
|
|
|
false, false, e.NewIndex != listposition, false);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* the deleted row was after the current one, so we don't
|
|
|
|
need to update bound controls 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) {
|
2014-09-04 09:07:35 +01:00
|
|
|
ChangeRecordState (listposition + 1,
|
2014-08-13 10:39:27 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|