28 changed files with 640 additions and 579 deletions
@ -1,153 +0,0 @@ |
|||||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
|
||||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.Collections; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.Collections.ObjectModel; |
|
||||||
using System.Collections.Specialized; |
|
||||||
using System.Linq; |
|
||||||
|
|
||||||
namespace ICSharpCode.SharpDevelop.Dom |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// A <see cref="IModelCollection{T}"/> that works by concatening multiple
|
|
||||||
/// other model collections.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class ConcatModelCollection<T> : IModelCollection<T> |
|
||||||
{ |
|
||||||
sealed class InputCollection : Collection<IModelCollection<T>> |
|
||||||
{ |
|
||||||
readonly ConcatModelCollection<T> owner; |
|
||||||
|
|
||||||
public InputCollection(ConcatModelCollection<T> owner) |
|
||||||
{ |
|
||||||
this.owner = owner; |
|
||||||
} |
|
||||||
|
|
||||||
protected override void ClearItems() |
|
||||||
{ |
|
||||||
if (owner.collectionChanged != null) { |
|
||||||
foreach (var input in Items) { |
|
||||||
input.CollectionChanged -= owner.OnInputCollectionChanged; |
|
||||||
} |
|
||||||
} |
|
||||||
base.ClearItems(); |
|
||||||
owner.RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); |
|
||||||
} |
|
||||||
|
|
||||||
protected override void InsertItem(int index, IModelCollection<T> item) |
|
||||||
{ |
|
||||||
if (owner.collectionChanged != null) |
|
||||||
item.CollectionChanged += owner.OnInputCollectionChanged; |
|
||||||
base.InsertItem(index, item); |
|
||||||
owner.RaiseCollectionChanged(new NotifyCollectionChangedEventArgs( |
|
||||||
NotifyCollectionChangedAction.Add, item.ToArray(), owner.GetCount(index))); |
|
||||||
} |
|
||||||
|
|
||||||
protected override void RemoveItem(int index) |
|
||||||
{ |
|
||||||
if (owner.collectionChanged != null) |
|
||||||
Items[index].CollectionChanged -= owner.OnInputCollectionChanged; |
|
||||||
var oldItems = Items[index].ToArray(); |
|
||||||
base.RemoveItem(index); |
|
||||||
owner.RaiseCollectionChanged(new NotifyCollectionChangedEventArgs( |
|
||||||
NotifyCollectionChangedAction.Remove, oldItems, owner.GetCount(index))); |
|
||||||
} |
|
||||||
|
|
||||||
protected override void SetItem(int index, IModelCollection<T> item) |
|
||||||
{ |
|
||||||
RemoveItem(index); |
|
||||||
InsertItem(index, item); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
InputCollection inputs; |
|
||||||
|
|
||||||
public ConcatModelCollection() |
|
||||||
{ |
|
||||||
this.inputs = new InputCollection(this); |
|
||||||
} |
|
||||||
|
|
||||||
public IList<IModelCollection<T>> Inputs { |
|
||||||
get { return inputs; } |
|
||||||
} |
|
||||||
|
|
||||||
NotifyCollectionChangedEventHandler collectionChanged; |
|
||||||
|
|
||||||
public event NotifyCollectionChangedEventHandler CollectionChanged { |
|
||||||
add { |
|
||||||
var oldEventHandlers = collectionChanged; |
|
||||||
collectionChanged += value; |
|
||||||
if (oldEventHandlers == null && collectionChanged != null) { |
|
||||||
foreach (var input in inputs) |
|
||||||
input.CollectionChanged += OnInputCollectionChanged; |
|
||||||
} |
|
||||||
} |
|
||||||
remove { |
|
||||||
var oldEventHandlers = collectionChanged; |
|
||||||
collectionChanged -= value; |
|
||||||
if (oldEventHandlers != null && collectionChanged == null) { |
|
||||||
foreach (var input in inputs) |
|
||||||
input.CollectionChanged -= OnInputCollectionChanged; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e) |
|
||||||
{ |
|
||||||
if (collectionChanged != null) |
|
||||||
collectionChanged(this, e); |
|
||||||
} |
|
||||||
|
|
||||||
void OnInputCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||||
{ |
|
||||||
int inputIndex = inputs.IndexOf((IModelCollection<T>)sender); |
|
||||||
int startIndex = GetCount(inputIndex); |
|
||||||
NotifyCollectionChangedEventArgs newEventArgs; |
|
||||||
switch (e.Action) { |
|
||||||
case NotifyCollectionChangedAction.Add: |
|
||||||
newEventArgs = new NotifyCollectionChangedEventArgs(e.Action, e.NewItems, startIndex + e.NewStartingIndex); |
|
||||||
break; |
|
||||||
case NotifyCollectionChangedAction.Remove: |
|
||||||
newEventArgs = new NotifyCollectionChangedEventArgs(e.Action, e.OldItems, startIndex + e.OldStartingIndex); |
|
||||||
break; |
|
||||||
case NotifyCollectionChangedAction.Replace: |
|
||||||
newEventArgs = new NotifyCollectionChangedEventArgs(e.Action, e.OldItems, e.NewItems, startIndex + e.OldStartingIndex); |
|
||||||
break; |
|
||||||
case NotifyCollectionChangedAction.Move: |
|
||||||
newEventArgs = new NotifyCollectionChangedEventArgs(e.Action, e.OldItems, startIndex + e.OldStartingIndex, startIndex + e.NewStartingIndex); |
|
||||||
break; |
|
||||||
case NotifyCollectionChangedAction.Reset: |
|
||||||
newEventArgs = new NotifyCollectionChangedEventArgs(e.Action); |
|
||||||
break; |
|
||||||
default: |
|
||||||
throw new NotSupportedException("Invalid value for NotifyCollectionChangedAction"); |
|
||||||
} |
|
||||||
collectionChanged(this, newEventArgs); |
|
||||||
} |
|
||||||
|
|
||||||
public int Count { |
|
||||||
get { return GetCount(inputs.Count); } |
|
||||||
} |
|
||||||
|
|
||||||
int GetCount(int inputIndex) |
|
||||||
{ |
|
||||||
int count = 0; |
|
||||||
for (int i = 0; i < inputIndex; i++) { |
|
||||||
count += inputs[i].Count; |
|
||||||
} |
|
||||||
return count; |
|
||||||
} |
|
||||||
|
|
||||||
public IEnumerator<T> GetEnumerator() |
|
||||||
{ |
|
||||||
return inputs.SelectMany(i => i).GetEnumerator(); |
|
||||||
} |
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() |
|
||||||
{ |
|
||||||
return GetEnumerator(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,104 +0,0 @@ |
|||||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
|
||||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.Collections; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.Collections.Specialized; |
|
||||||
using System.Linq; |
|
||||||
|
|
||||||
namespace ICSharpCode.SharpDevelop.Dom |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// A model collection that filters an input collection.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class FilterModelCollection<T> : IModelCollection<T> |
|
||||||
{ |
|
||||||
readonly IModelCollection<T> input; |
|
||||||
readonly Func<T, bool> predicate; |
|
||||||
bool isAttached; |
|
||||||
NotifyCollectionChangedEventHandler collectionChanged; |
|
||||||
|
|
||||||
public FilterModelCollection(IModelCollection<T> input, Func<T, bool> predicate) |
|
||||||
{ |
|
||||||
if (input == null) |
|
||||||
throw new ArgumentNullException("input"); |
|
||||||
if (predicate == null) |
|
||||||
throw new ArgumentNullException("predicate"); |
|
||||||
this.input = input; |
|
||||||
this.predicate = predicate; |
|
||||||
} |
|
||||||
|
|
||||||
public event NotifyCollectionChangedEventHandler CollectionChanged { |
|
||||||
add { |
|
||||||
collectionChanged += value; |
|
||||||
if (collectionChanged != null && !isAttached) { |
|
||||||
input.CollectionChanged += input_CollectionChanged; |
|
||||||
isAttached = true; |
|
||||||
} |
|
||||||
} |
|
||||||
remove { |
|
||||||
collectionChanged -= value; |
|
||||||
if (collectionChanged == null && isAttached) { |
|
||||||
input.CollectionChanged -= input_CollectionChanged; |
|
||||||
isAttached = false; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void input_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|
||||||
{ |
|
||||||
switch (e.Action) { |
|
||||||
case NotifyCollectionChangedAction.Add: |
|
||||||
collectionChanged(this, new NotifyCollectionChangedEventArgs( |
|
||||||
NotifyCollectionChangedAction.Add, ApplyFilter(e.NewItems))); |
|
||||||
break; |
|
||||||
case NotifyCollectionChangedAction.Remove: |
|
||||||
collectionChanged(this, new NotifyCollectionChangedEventArgs( |
|
||||||
NotifyCollectionChangedAction.Remove, ApplyFilter(e.OldItems))); |
|
||||||
break; |
|
||||||
case NotifyCollectionChangedAction.Replace: |
|
||||||
collectionChanged(this, new NotifyCollectionChangedEventArgs( |
|
||||||
NotifyCollectionChangedAction.Replace, ApplyFilter(e.OldItems), ApplyFilter(e.NewItems))); |
|
||||||
break; |
|
||||||
case NotifyCollectionChangedAction.Move: |
|
||||||
// this collection is unordered
|
|
||||||
break; |
|
||||||
case NotifyCollectionChangedAction.Reset: |
|
||||||
collectionChanged(this, new NotifyCollectionChangedEventArgs( |
|
||||||
NotifyCollectionChangedAction.Reset)); |
|
||||||
break; |
|
||||||
default: |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
IList ApplyFilter(IList inputItems) |
|
||||||
{ |
|
||||||
if (inputItems == null) |
|
||||||
return null; |
|
||||||
List<T> outputItems = new List<T>(); |
|
||||||
foreach (T item in inputItems) { |
|
||||||
if (predicate(item)) |
|
||||||
outputItems.Add(item); |
|
||||||
} |
|
||||||
return outputItems; |
|
||||||
} |
|
||||||
|
|
||||||
public int Count { |
|
||||||
get { |
|
||||||
return input.Count(predicate); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public IEnumerator<T> GetEnumerator() |
|
||||||
{ |
|
||||||
return input.Where(predicate).GetEnumerator(); |
|
||||||
} |
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() |
|
||||||
{ |
|
||||||
return GetEnumerator(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,54 +0,0 @@ |
|||||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
|
||||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.Collections.ObjectModel; |
|
||||||
using System.Collections.Specialized; |
|
||||||
|
|
||||||
namespace ICSharpCode.SharpDevelop.Dom |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Observable KeyedCollection.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class KeyedModelCollection<TKey, TItem> : KeyedCollection<TKey, TItem>, IModelCollection<TItem> |
|
||||||
{ |
|
||||||
// TODO: do we still need this class? maybe we should remove it?
|
|
||||||
|
|
||||||
public bool TryGetValue(TKey key, out TItem item) |
|
||||||
{ |
|
||||||
return Dictionary.TryGetValue(key, out item); |
|
||||||
} |
|
||||||
|
|
||||||
protected override void ClearItems() |
|
||||||
{ |
|
||||||
base.ClearItems(); |
|
||||||
if (CollectionChanged != null) |
|
||||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); |
|
||||||
} |
|
||||||
|
|
||||||
protected override void InsertItem(int index, TItem item) |
|
||||||
{ |
|
||||||
base.InsertItem(index, item); |
|
||||||
if (CollectionChanged != null) |
|
||||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); |
|
||||||
} |
|
||||||
|
|
||||||
protected override void RemoveItem(int index) |
|
||||||
{ |
|
||||||
var oldItem = Items[index]; |
|
||||||
base.RemoveItem(index); |
|
||||||
if (CollectionChanged != null) |
|
||||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index)); |
|
||||||
} |
|
||||||
|
|
||||||
protected override void SetItem(int index, TItem item) |
|
||||||
{ |
|
||||||
var oldItem = Items[index]; |
|
||||||
base.SetItem(index, item); |
|
||||||
if (CollectionChanged != null) |
|
||||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index)); |
|
||||||
} |
|
||||||
|
|
||||||
public event NotifyCollectionChangedEventHandler CollectionChanged; |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,191 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Collections.ObjectModel; |
||||||
|
using System.Linq; |
||||||
|
using System.Runtime.InteropServices.WindowsRuntime; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Dom |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Provides LINQ operators for .
|
||||||
|
/// </summary>
|
||||||
|
public static class ModelCollectionLinq |
||||||
|
{ |
||||||
|
// A general 'AsObservableCollection()' would be nice; but I don't see any good way
|
||||||
|
// to implement that without leaking memory.
|
||||||
|
// The problem is that IModelCollection is unordered; but ObservableCollection requires us to maintain a stable order.
|
||||||
|
|
||||||
|
#region Where
|
||||||
|
/*public static IModelCollection<TSource> Where<TSource>(this IModelCollection<TSource> source, Func<TSource, bool> predicate) |
||||||
|
{ |
||||||
|
|
||||||
|
}*/ |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Select
|
||||||
|
public static IModelCollection<TResult> Select<TSource, TResult>(this IModelCollection<TSource> source, Func<TSource, TResult> selector) |
||||||
|
{ |
||||||
|
// HACK: emulating Select with SelectMany is much less efficient than a direct implementation could be
|
||||||
|
return SelectMany(source, item => new ReadOnlyModelCollection<TSource>(new[] { item }), (a, b) => selector(b)); |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region SelectMany
|
||||||
|
public static IModelCollection<S> SelectMany<T, S>(this IModelCollection<T> input, Func<T, IModelCollection<S>> selector) |
||||||
|
{ |
||||||
|
return SelectMany(input, selector, (a, b) => b); |
||||||
|
} |
||||||
|
|
||||||
|
public static IModelCollection<TResult> SelectMany<TSource, TCollection, TResult>(this IModelCollection<TSource> source, Func<TSource, IModelCollection<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector) |
||||||
|
{ |
||||||
|
if (source == null) |
||||||
|
throw new ArgumentNullException("source"); |
||||||
|
if (collectionSelector == null) |
||||||
|
throw new ArgumentNullException("collectionSelector"); |
||||||
|
if (resultSelector == null) |
||||||
|
throw new ArgumentNullException("resultSelector"); |
||||||
|
return new SelectManyModelCollection<TSource, TCollection, TResult>(source, collectionSelector, resultSelector); |
||||||
|
} |
||||||
|
|
||||||
|
sealed class SelectManyModelCollection<TSource, TCollection, TResult> : IModelCollection<TResult> |
||||||
|
{ |
||||||
|
readonly IModelCollection<TSource> source; |
||||||
|
readonly Func<TSource, IModelCollection<TCollection>> collectionSelector; |
||||||
|
readonly Func<TSource, TCollection, TResult> resultSelector; |
||||||
|
List<InputCollection> inputCollections; |
||||||
|
|
||||||
|
public SelectManyModelCollection(IModelCollection<TSource> source, Func<TSource, IModelCollection<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector) |
||||||
|
{ |
||||||
|
this.source = source; |
||||||
|
this.collectionSelector = collectionSelector; |
||||||
|
this.resultSelector = resultSelector; |
||||||
|
} |
||||||
|
|
||||||
|
ModelCollectionChangedEventHandler<TResult> collectionChanged; |
||||||
|
|
||||||
|
public event ModelCollectionChangedEventHandler<TResult> CollectionChanged { |
||||||
|
add { |
||||||
|
if (value == null) |
||||||
|
return; |
||||||
|
if (inputCollections == null) { |
||||||
|
source.CollectionChanged += OnSourceCollectionChanged; |
||||||
|
inputCollections = new List<InputCollection>(); |
||||||
|
foreach (var item in source) { |
||||||
|
var inputCollection = new InputCollection(this, item); |
||||||
|
inputCollection.RegisterEvent(); |
||||||
|
inputCollections.Add(inputCollection); |
||||||
|
} |
||||||
|
} |
||||||
|
collectionChanged += value; |
||||||
|
} |
||||||
|
remove { |
||||||
|
if (collectionChanged == null) |
||||||
|
return; |
||||||
|
collectionChanged -= value; |
||||||
|
if (collectionChanged == null) { |
||||||
|
source.CollectionChanged -= OnSourceCollectionChanged; |
||||||
|
foreach (var inputCollection in inputCollections) { |
||||||
|
inputCollection.UnregisterEvent(); |
||||||
|
} |
||||||
|
inputCollections = null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void OnCollectionChanged(IReadOnlyCollection<TResult> removedItems, IReadOnlyCollection<TResult> addedItems) |
||||||
|
{ |
||||||
|
if (collectionChanged != null) |
||||||
|
collectionChanged(removedItems, addedItems); |
||||||
|
} |
||||||
|
|
||||||
|
void OnSourceCollectionChanged(IReadOnlyCollection<TSource> removedItems, IReadOnlyCollection<TSource> addedItems) |
||||||
|
{ |
||||||
|
List<TResult> removedResults = new List<TResult>(); |
||||||
|
foreach (TSource removedSource in removedItems) { |
||||||
|
for (int i = 0; i < inputCollections.Count; i++) { |
||||||
|
var inputCollection = inputCollections[i]; |
||||||
|
if (EqualityComparer<TSource>.Default.Equals(inputCollection.source, removedSource)) { |
||||||
|
inputCollection.AddResultsToList(removedResults); |
||||||
|
inputCollection.UnregisterEvent(); |
||||||
|
inputCollections.RemoveAt(i); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
List<TResult> addedResults = new List<TResult>(); |
||||||
|
foreach (TSource addedSource in addedItems) { |
||||||
|
var inputCollection = new InputCollection(this, addedSource); |
||||||
|
inputCollection.AddResultsToList(addedResults); |
||||||
|
inputCollection.RegisterEvent(); |
||||||
|
inputCollections.Add(inputCollection); |
||||||
|
} |
||||||
|
OnCollectionChanged(removedResults, addedResults); |
||||||
|
} |
||||||
|
|
||||||
|
class InputCollection |
||||||
|
{ |
||||||
|
readonly SelectManyModelCollection<TSource, TCollection, TResult> parent; |
||||||
|
internal readonly TSource source; |
||||||
|
readonly IModelCollection<TCollection> collection; |
||||||
|
|
||||||
|
public InputCollection(SelectManyModelCollection<TSource, TCollection, TResult> parent, TSource source) |
||||||
|
{ |
||||||
|
this.parent = parent; |
||||||
|
this.source = source; |
||||||
|
this.collection = parent.collectionSelector(source); |
||||||
|
} |
||||||
|
|
||||||
|
public void AddResultsToList(List<TResult> output) |
||||||
|
{ |
||||||
|
foreach (var item in collection) { |
||||||
|
output.Add(parent.resultSelector(source, item)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void RegisterEvent() |
||||||
|
{ |
||||||
|
collection.CollectionChanged += OnCollectionChanged; |
||||||
|
} |
||||||
|
|
||||||
|
public void UnregisterEvent() |
||||||
|
{ |
||||||
|
collection.CollectionChanged -= OnCollectionChanged; |
||||||
|
} |
||||||
|
|
||||||
|
IReadOnlyCollection<TResult> GetResults(IReadOnlyCollection<TCollection> itemsCollection) |
||||||
|
{ |
||||||
|
List<TResult> results = new List<TResult>(itemsCollection.Count); |
||||||
|
foreach (var item in itemsCollection) { |
||||||
|
results.Add(parent.resultSelector(source, item)); |
||||||
|
} |
||||||
|
return results; |
||||||
|
} |
||||||
|
|
||||||
|
void OnCollectionChanged(IReadOnlyCollection<TCollection> removedItems, IReadOnlyCollection<TCollection> addedItems) |
||||||
|
{ |
||||||
|
parent.OnCollectionChanged(GetResults(removedItems), GetResults(addedItems)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IEnumerator<TResult> GetEnumerator() |
||||||
|
{ |
||||||
|
return source.AsEnumerable().SelectMany(collectionSelector, resultSelector).GetEnumerator(); |
||||||
|
} |
||||||
|
|
||||||
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
||||||
|
{ |
||||||
|
return GetEnumerator(); |
||||||
|
} |
||||||
|
|
||||||
|
public int Count { |
||||||
|
get { |
||||||
|
return source.Sum(c => collectionSelector(c).Count); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)using System;
|
||||||
|
|
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Collections.ObjectModel; |
||||||
|
using System.Linq; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Dom |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A model collection implementation that is based on a ReadOnlyCollection.
|
||||||
|
/// </summary>
|
||||||
|
public class ReadOnlyModelCollection<T> : ReadOnlyCollection<T>, IModelCollection<T> |
||||||
|
{ |
||||||
|
public ReadOnlyModelCollection(IEnumerable<T> items) |
||||||
|
: base(items.ToList()) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
event ModelCollectionChangedEventHandler<T> IModelCollection<T>.CollectionChanged { add {} remove {} } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,206 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)using System;
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Collections.ObjectModel; |
||||||
|
using System.Collections.Specialized; |
||||||
|
using System.Linq; |
||||||
|
using ICSharpCode.NRefactory; |
||||||
|
using ICSharpCode.NRefactory.Utils; |
||||||
|
|
||||||
|
namespace ICSharpCode.SharpDevelop.Dom |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A model collection implementation.
|
||||||
|
/// </summary>
|
||||||
|
public class SimpleModelCollection<T> : IMutableModelCollection<T> |
||||||
|
{ |
||||||
|
readonly List<T> list; |
||||||
|
List<T> addedItems; |
||||||
|
List<T> removedItems; |
||||||
|
|
||||||
|
public SimpleModelCollection() |
||||||
|
{ |
||||||
|
this.list = new List<T>(); |
||||||
|
} |
||||||
|
|
||||||
|
public SimpleModelCollection(IEnumerable<T> items) |
||||||
|
{ |
||||||
|
this.list = new List<T>(items); |
||||||
|
} |
||||||
|
|
||||||
|
protected void CheckReentrancy() |
||||||
|
{ |
||||||
|
if (isRaisingEvent) |
||||||
|
throw new InvalidOperationException("Cannot modify the collection from within the CollectionChanged event."); |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual void ValidateItem(T item) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
#region CollectionChanged / BatchUpdate()
|
||||||
|
public event ModelCollectionChangedEventHandler<T> CollectionChanged; |
||||||
|
|
||||||
|
bool isWithinBatchOperation; |
||||||
|
bool isRaisingEvent; |
||||||
|
|
||||||
|
void RaiseEventIfNotInBatch() |
||||||
|
{ |
||||||
|
if (isWithinBatchOperation) |
||||||
|
return; |
||||||
|
IReadOnlyCollection<T> removed = this.removedItems; |
||||||
|
IReadOnlyCollection<T> added = this.addedItems; |
||||||
|
this.removedItems = null; |
||||||
|
this.addedItems = null; |
||||||
|
this.isRaisingEvent = true; |
||||||
|
try { |
||||||
|
OnCollectionChanged(removed ?? EmptyList<T>.Instance, added ?? EmptyList<T>.Instance); |
||||||
|
} finally { |
||||||
|
this.isRaisingEvent = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual void OnCollectionChanged(IReadOnlyCollection<T> removedItems, IReadOnlyCollection<T> addedItems) |
||||||
|
{ |
||||||
|
var handler = CollectionChanged; |
||||||
|
if (handler != null) |
||||||
|
handler(removedItems, addedItems); |
||||||
|
} |
||||||
|
|
||||||
|
public IDisposable BatchUpdate() |
||||||
|
{ |
||||||
|
if (isWithinBatchOperation) |
||||||
|
return null; |
||||||
|
isWithinBatchOperation = true; |
||||||
|
return new CallbackOnDispose( |
||||||
|
delegate { |
||||||
|
isWithinBatchOperation = false; |
||||||
|
if (removedItems != null || addedItems != null) |
||||||
|
RaiseEventIfNotInBatch(); |
||||||
|
}); |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Read-Only list access
|
||||||
|
|
||||||
|
public int Count { |
||||||
|
get { return list.Count; } |
||||||
|
} |
||||||
|
|
||||||
|
public bool Contains(T item) |
||||||
|
{ |
||||||
|
return list.Contains(item); |
||||||
|
} |
||||||
|
|
||||||
|
public void CopyTo(T[] array, int arrayIndex) |
||||||
|
{ |
||||||
|
list.CopyTo(array, arrayIndex); |
||||||
|
} |
||||||
|
|
||||||
|
bool ICollection<T>.IsReadOnly { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
public IEnumerator<T> GetEnumerator() |
||||||
|
{ |
||||||
|
return list.GetEnumerator(); |
||||||
|
} |
||||||
|
|
||||||
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
||||||
|
{ |
||||||
|
return list.GetEnumerator(); |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IMutableModelCollection implementation
|
||||||
|
|
||||||
|
public void Clear() |
||||||
|
{ |
||||||
|
CheckReentrancy(); |
||||||
|
addedItems = null; |
||||||
|
if (removedItems == null) |
||||||
|
removedItems = new List<T>(); |
||||||
|
removedItems.AddRange(list); |
||||||
|
list.Clear(); |
||||||
|
RaiseEventIfNotInBatch(); |
||||||
|
} |
||||||
|
|
||||||
|
public void Add(T item) |
||||||
|
{ |
||||||
|
CheckReentrancy(); |
||||||
|
ValidateItem(item); |
||||||
|
if (removedItems != null) |
||||||
|
removedItems.Remove(item); |
||||||
|
if (addedItems == null) |
||||||
|
addedItems = new List<T>(); |
||||||
|
addedItems.Add(item); |
||||||
|
list.Add(item); |
||||||
|
RaiseEventIfNotInBatch(); |
||||||
|
} |
||||||
|
|
||||||
|
public void AddRange(IEnumerable<T> items) |
||||||
|
{ |
||||||
|
if (items == null) |
||||||
|
throw new ArgumentNullException("items"); |
||||||
|
CheckReentrancy(); |
||||||
|
List<T> itemsList = items.ToList(); |
||||||
|
for (int i = 0; i < itemsList.Count; i++) { |
||||||
|
ValidateItem(itemsList[i]); |
||||||
|
} |
||||||
|
if (removedItems != null) { |
||||||
|
for (int i = 0; i < itemsList.Count; i++) { |
||||||
|
removedItems.Remove(itemsList[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
if (addedItems != null) |
||||||
|
addedItems.AddRange(itemsList); |
||||||
|
else |
||||||
|
addedItems = itemsList; |
||||||
|
list.AddRange(itemsList); |
||||||
|
RaiseEventIfNotInBatch(); |
||||||
|
} |
||||||
|
|
||||||
|
public bool Remove(T item) |
||||||
|
{ |
||||||
|
CheckReentrancy(); |
||||||
|
if (list.Remove(item)) { |
||||||
|
if (addedItems != null) |
||||||
|
addedItems.Remove(item); |
||||||
|
if (removedItems == null) |
||||||
|
removedItems = new List<T>(); |
||||||
|
removedItems.Add(item); |
||||||
|
RaiseEventIfNotInBatch(); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public int RemoveAll(Predicate<T> predicate) |
||||||
|
{ |
||||||
|
CheckReentrancy(); |
||||||
|
int count = list.RemoveAll( |
||||||
|
delegate(T obj) { |
||||||
|
if (predicate(obj)) { |
||||||
|
if (addedItems != null) |
||||||
|
addedItems.Remove(obj); |
||||||
|
if (removedItems == null) |
||||||
|
removedItems = new List<T>(); |
||||||
|
removedItems.Add(obj); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
}); |
||||||
|
if (count > 0) |
||||||
|
RaiseEventIfNotInBatch(); |
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
Loading…
Reference in new issue