You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
6.2 KiB

  1. // Copyright (c) 2020 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Collections.Specialized;
  21. using System.Diagnostics;
  22. using System.Linq;
  23. #nullable disable
  24. namespace ICSharpCode.ILSpyX.TreeView
  25. {
  26. /// <summary>
  27. /// Collection that validates that inserted nodes do not have another parent.
  28. /// </summary>
  29. public sealed class SharpTreeNodeCollection : IList<SharpTreeNode>, INotifyCollectionChanged
  30. {
  31. readonly SharpTreeNode parent;
  32. List<SharpTreeNode> list = new List<SharpTreeNode>();
  33. bool isRaisingEvent;
  34. public SharpTreeNodeCollection(SharpTreeNode parent)
  35. {
  36. this.parent = parent;
  37. }
  38. public event NotifyCollectionChangedEventHandler CollectionChanged;
  39. void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
  40. {
  41. Debug.Assert(!isRaisingEvent);
  42. isRaisingEvent = true;
  43. try
  44. {
  45. parent.OnChildrenChanged(e);
  46. CollectionChanged?.Invoke(this, e);
  47. }
  48. finally
  49. {
  50. isRaisingEvent = false;
  51. }
  52. }
  53. void ThrowOnReentrancy()
  54. {
  55. if (isRaisingEvent)
  56. throw new InvalidOperationException();
  57. }
  58. void ThrowIfValueIsNullOrHasParent(SharpTreeNode node)
  59. {
  60. if (node == null)
  61. throw new ArgumentNullException("node");
  62. if (node.modelParent != null)
  63. throw new ArgumentException("The node already has a parent", "node");
  64. }
  65. public SharpTreeNode this[int index] {
  66. get {
  67. return list[index];
  68. }
  69. set {
  70. ThrowOnReentrancy();
  71. var oldItem = list[index];
  72. if (oldItem == value)
  73. return;
  74. ThrowIfValueIsNullOrHasParent(value);
  75. list[index] = value;
  76. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldItem, index));
  77. }
  78. }
  79. public int Count {
  80. get { return list.Count; }
  81. }
  82. bool ICollection<SharpTreeNode>.IsReadOnly {
  83. get { return false; }
  84. }
  85. public int IndexOf(SharpTreeNode node)
  86. {
  87. if (node == null || node.modelParent != parent)
  88. return -1;
  89. else
  90. return list.IndexOf(node);
  91. }
  92. public void Insert(int index, SharpTreeNode node)
  93. {
  94. ThrowOnReentrancy();
  95. ThrowIfValueIsNullOrHasParent(node);
  96. list.Insert(index, node);
  97. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, index));
  98. }
  99. public void InsertRange(int index, IEnumerable<SharpTreeNode> nodes)
  100. {
  101. if (nodes == null)
  102. throw new ArgumentNullException("nodes");
  103. ThrowOnReentrancy();
  104. List<SharpTreeNode> newNodes = nodes.ToList();
  105. if (newNodes.Count == 0)
  106. return;
  107. foreach (SharpTreeNode node in newNodes)
  108. {
  109. ThrowIfValueIsNullOrHasParent(node);
  110. }
  111. list.InsertRange(index, newNodes);
  112. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newNodes, index));
  113. }
  114. public void RemoveAt(int index)
  115. {
  116. ThrowOnReentrancy();
  117. var oldItem = list[index];
  118. list.RemoveAt(index);
  119. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index));
  120. }
  121. public void RemoveRange(int index, int count)
  122. {
  123. ThrowOnReentrancy();
  124. if (count == 0)
  125. return;
  126. var oldItems = list.GetRange(index, count);
  127. list.RemoveRange(index, count);
  128. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems, index));
  129. }
  130. public void Add(SharpTreeNode node)
  131. {
  132. ThrowOnReentrancy();
  133. ThrowIfValueIsNullOrHasParent(node);
  134. list.Add(node);
  135. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, node, list.Count - 1));
  136. }
  137. public void AddRange(IEnumerable<SharpTreeNode> nodes)
  138. {
  139. InsertRange(this.Count, nodes);
  140. }
  141. public void Clear()
  142. {
  143. ThrowOnReentrancy();
  144. var oldList = list;
  145. list = new List<SharpTreeNode>();
  146. OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldList, 0));
  147. }
  148. public bool Contains(SharpTreeNode node)
  149. {
  150. return IndexOf(node) >= 0;
  151. }
  152. public void CopyTo(SharpTreeNode[] array, int arrayIndex)
  153. {
  154. list.CopyTo(array, arrayIndex);
  155. }
  156. public bool Remove(SharpTreeNode item)
  157. {
  158. int pos = IndexOf(item);
  159. if (pos >= 0)
  160. {
  161. RemoveAt(pos);
  162. return true;
  163. }
  164. else
  165. {
  166. return false;
  167. }
  168. }
  169. public IEnumerator<SharpTreeNode> GetEnumerator()
  170. {
  171. return list.GetEnumerator();
  172. }
  173. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  174. {
  175. return list.GetEnumerator();
  176. }
  177. public void RemoveAll(Predicate<SharpTreeNode> match)
  178. {
  179. if (match == null)
  180. throw new ArgumentNullException("match");
  181. ThrowOnReentrancy();
  182. int firstToRemove = 0;
  183. for (int i = 0; i < list.Count; i++)
  184. {
  185. bool removeNode;
  186. isRaisingEvent = true;
  187. try
  188. {
  189. removeNode = match(list[i]);
  190. }
  191. finally
  192. {
  193. isRaisingEvent = false;
  194. }
  195. if (!removeNode)
  196. {
  197. if (firstToRemove < i)
  198. {
  199. RemoveRange(firstToRemove, i - firstToRemove);
  200. i = firstToRemove - 1;
  201. }
  202. else
  203. {
  204. firstToRemove = i + 1;
  205. }
  206. Debug.Assert(firstToRemove == i + 1);
  207. }
  208. }
  209. if (firstToRemove < list.Count)
  210. {
  211. RemoveRange(firstToRemove, list.Count - firstToRemove);
  212. }
  213. }
  214. }
  215. }