Эх сурвалжийг харах

Merge branch '3.8' of https://github.com/EsotericSoftware/spine-runtimes into 3.8

badlogic 6 жил өмнө
parent
commit
84c09c1bbc
20 өөрчлөгдсөн 720 нэмэгдсэн , 190 устгасан
  1. 2 1
      CHANGELOG.md
  2. 631 0
      spine-csharp/src/Collections/OrderedDictionary.cs
  3. 1 1
      spine-csharp/src/SkeletonBinary.cs
  4. 11 16
      spine-csharp/src/Skin.cs
  5. 11 28
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SkeletonDataAssetInspector.cs
  6. 1 1
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/PointFollowerInspector.cs
  7. 4 8
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonUtilityBoneInspector.cs
  8. 0 8
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs
  9. 0 4
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/BuildSettings.cs
  10. 0 12
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/DataReloadHandler.cs
  11. 0 16
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/Icons.cs
  12. 0 16
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/Instantiation.cs
  13. 15 7
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/Preferences.cs
  14. 0 4
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineEditorUtilities.cs
  15. 0 16
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineHandles.cs
  16. 10 19
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBaker.cs
  17. 8 16
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonDebugWindow.cs
  18. 14 5
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SpinePreferences.cs
  19. 3 3
      spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs
  20. 9 9
      spine-unity/Assets/Spine/Runtime/spine-unity/Utility/SkinUtilities.cs

+ 2 - 1
CHANGELOG.md

@@ -108,6 +108,7 @@
   * Renamed `Slot.AttachmentVertices` to `Slot.Deform`.
   * Renamed `Slot.AttachmentVertices` to `Slot.Deform`.
   * Changed the `.json` curve format and added more assumptions for omitted values, reducing the average size of JSON exports.
   * Changed the `.json` curve format and added more assumptions for omitted values, reducing the average size of JSON exports.
   * Renamed `Skin.AddAttachment()` to `Skin.SetAttachment()`.
   * Renamed `Skin.AddAttachment()` to `Skin.SetAttachment()`.
+  * Removed `FindAttachmentsForSlot(int slotIndex, List<Attachment> attachments)` and `FindNamesForSlot (int slotIndex, List<string> names)` and replaced it with `Skin.GetAttachments(int slotIndex, List<SkinEntry> attachments)` which returns the combined `SkinEntry` object holding both name and attachment.
   * Removed `VertexAttachment.ApplyDeform()` and replaced it with `VertexAttachment.DeformAttachment`. The attachment set on this field is used to decide if a `DeformTimeline` should be applied to the attachment active on the slot to which the timeline is applied.
   * Removed `VertexAttachment.ApplyDeform()` and replaced it with `VertexAttachment.DeformAttachment`. The attachment set on this field is used to decide if a `DeformTimeline` should be applied to the attachment active on the slot to which the timeline is applied.
   * Removed `inheritDeform` field, getter, and setter from `MeshAttachment`.
   * Removed `inheritDeform` field, getter, and setter from `MeshAttachment`.
   * Changed `.skel` binary format, added a string table. References to strings in the data resolve to this string table, reducing storage size of binary files considerably.
   * Changed `.skel` binary format, added a string table. References to strings in the data resolve to this string table, reducing storage size of binary files considerably.
@@ -119,7 +120,7 @@
   * Added skin-specific bones and constraints which are only updated if the skeleton's current skin contains them.
   * Added skin-specific bones and constraints which are only updated if the skeleton's current skin contains them.
   * Improved Skin API to make it easier to handle mix-and-match use cases.
   * Improved Skin API to make it easier to handle mix-and-match use cases.
     * Added `Skin.GetAttachments()`. Returns all attachments in the skin.
     * Added `Skin.GetAttachments()`. Returns all attachments in the skin.
-    * Added `Skin.GetAttachments(int slotIndex, List<SkinEntry> attachments)`. Returns all attachements in the skin for the given slot index.
+    * Added `Skin.GetAttachments(int slotIndex, List<SkinEntry> attachments)`. Returns all attachements in the skin for the given slot index. This method replaces `FindAttachmentsForSlot` and `FindNamesForSlot`.
     * Added `Skin.AddSkin(Skin skin)`. Adds all attachments, bones, and skins from the specified skin to this skin.
     * Added `Skin.AddSkin(Skin skin)`. Adds all attachments, bones, and skins from the specified skin to this skin.
     * Added `Skin.CopySkin(Skin skin)`. Adds all attachments, bones, and skins from the specified skin to this skin. `VertexAttachment` are shallowly copied and will retain any parent mesh relationship. All other attachment types are deep copied.
     * Added `Skin.CopySkin(Skin skin)`. Adds all attachments, bones, and skins from the specified skin to this skin. `VertexAttachment` are shallowly copied and will retain any parent mesh relationship. All other attachment types are deep copied.
   * Added `Attachment.Copy()` to all attachment type implementations. This lets you deep copy an attachment to modify it independently from the original, i.e. when programmatically changing texture coordinates or mesh vertices.
   * Added `Attachment.Copy()` to all attachment type implementations. This lets you deep copy an attachment to modify it independently from the original, i.e. when programmatically changing texture coordinates or mesh vertices.

+ 631 - 0
spine-csharp/src/Collections/OrderedDictionary.cs

@@ -0,0 +1,631 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated May 1, 2019. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2019, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS
+ * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+/******************************************************************************
+ * Thanks to Travis Parks
+ * https://github.com/jehugaleahsa/truncon.collections.OrderedDictionary
+ *****************************************************************************/
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+
+namespace Spine.Collections
+{
+	/// <summary>
+	/// Represents a dictionary that tracks the order that items were added.
+	/// </summary>
+	/// <typeparam name="TKey">The type of the dictionary keys.</typeparam>
+	/// <typeparam name="TValue">The type of the dictionary values.</typeparam>
+	/// <remarks>
+	/// This dictionary makes it possible to get the index of a key and a key based on an index.
+	/// It can be costly to find the index of a key because it must be searched for linearly.
+	/// It can be costly to insert a key/value pair because other key's indexes must be adjusted.
+	/// It can be costly to remove a key/value pair because other keys' indexes must be adjusted.
+	/// </remarks>
+	[DebuggerDisplay("Count = {Count}")]
+	[DebuggerTypeProxy(typeof(OrderedDictionaryDebugView<,>))]
+	public sealed class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IList<KeyValuePair<TKey, TValue>>
+	{
+		private readonly Dictionary<TKey, int> dictionary;
+		private readonly List<TKey> keys;
+		private readonly List<TValue> values;
+		private int version;
+
+		private const string CollectionModifiedMessage = "Collection was modified; enumeration operation may not execute.";
+		private const string EditReadOnlyListMessage = "An attempt was made to edit a read-only list.";
+		private const string IndexOutOfRangeMessage = "The index is negative or outside the bounds of the collection.";
+
+		/// <summary>
+		/// Initializes a new instance of an OrderedDictionary.
+		/// </summary>
+		public OrderedDictionary ()
+			: this(0, null) {
+		}
+
+		/// <summary>
+		/// Initializes a new instance of an OrderedDictionary.
+		/// </summary>
+		/// <param name="capacity">The initial capacity of the dictionary.</param>
+		/// <exception cref="System.ArgumentOutOfRangeException">The capacity is less than zero.</exception>
+		public OrderedDictionary (int capacity)
+			: this(capacity, null) {
+		}
+
+		/// <summary>
+		/// Initializes a new instance of an OrderedDictionary.
+		/// </summary>
+		/// <param name="comparer">The equality comparer to use to compare keys.</param>
+		public OrderedDictionary (IEqualityComparer<TKey> comparer)
+			: this(0, comparer) {
+		}
+
+		/// <summary>
+		/// Initializes a new instance of an OrderedDictionary.
+		/// </summary>
+		/// <param name="capacity">The initial capacity of the dictionary.</param>
+		/// <param name="comparer">The equality comparer to use to compare keys.</param>
+		public OrderedDictionary (int capacity, IEqualityComparer<TKey> comparer) {
+			dictionary = new Dictionary<TKey, int>(capacity, comparer ?? EqualityComparer<TKey>.Default);
+			keys = new List<TKey>(capacity);
+			values = new List<TValue>(capacity);
+		}
+
+		/// <summary>
+		/// Gets the equality comparer used to compare keys.
+		/// </summary>
+		public IEqualityComparer<TKey> Comparer {
+			get {
+				return dictionary.Comparer;
+			}
+		}
+
+		/// <summary>
+		/// Adds the given key/value pair to the dictionary.
+		/// </summary>
+		/// <param name="key">The key to add to the dictionary.</param>
+		/// <param name="value">The value to associated with the key.</param>
+		/// <exception cref="System.ArgumentException">The given key already exists in the dictionary.</exception>
+		/// <exception cref="System.ArgumentNullException">The key is null.</exception>
+		public void Add (TKey key, TValue value) {
+			dictionary.Add(key, values.Count);
+			keys.Add(key);
+			values.Add(value);
+			++version;
+		}
+
+		/// <summary>
+		/// Inserts the given key/value pair at the specified index.
+		/// </summary>
+		/// <param name="index">The index to insert the key/value pair.</param>
+		/// <param name="key">The key to insert.</param>
+		/// <param name="value">The value to insert.</param>
+		/// <exception cref="System.ArgumentException">The given key already exists in the dictionary.</exception>
+		/// <exception cref="System.ArgumentNullException">The key is null.</exception>
+		/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the size of the dictionary.</exception>
+		public void Insert (int index, TKey key, TValue value) {
+			if (index < 0 || index > values.Count) {
+				throw new ArgumentOutOfRangeException("index", index, IndexOutOfRangeMessage);
+			}
+			dictionary.Add(key, index);
+			for (int keyIndex = index; keyIndex != keys.Count; ++keyIndex) {
+				var otherKey = keys[keyIndex];
+				dictionary[otherKey] += 1;
+			}
+			keys.Insert(index, key);
+			values.Insert(index, value);
+			++version;
+		}
+
+		/// <summary>
+		/// Determines whether the given key exists in the dictionary.
+		/// </summary>
+		/// <param name="key">The key to look for.</param>
+		/// <returns>True if the key exists in the dictionary; otherwise, false.</returns>
+		/// <exception cref="System.ArgumentNullException">The key is null.</exception>
+		public bool ContainsKey (TKey key) {
+			return dictionary.ContainsKey(key);
+		}
+
+		/// <summary>
+		/// Gets the key at the given index.
+		/// </summary>
+		/// <param name="index">The index of the key to get.</param>
+		/// <returns>The key at the given index.</returns>
+		/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the number of keys.</exception>
+		public TKey GetKey (int index) {
+			return keys[index];
+		}
+
+		/// <summary>
+		/// Gets the index of the given key.
+		/// </summary>
+		/// <param name="key">The key to get the index of.</param>
+		/// <returns>The index of the key in the dictionary -or- -1 if the key is not found.</returns>
+		/// <remarks>The operation runs in O(n).</remarks>
+		public int IndexOf (TKey key) {
+			int index;
+			if (dictionary.TryGetValue(key, out index)) {
+				return index;
+			}
+			return -1;
+		}
+
+		/// <summary>
+		/// Gets the keys in the dictionary in the order they were added.
+		/// </summary>
+		public KeyCollection Keys {
+			get {
+				return new KeyCollection(this.dictionary);
+			}
+		}
+
+		/// <summary>
+		/// Removes the key/value pair with the given key from the dictionary.
+		/// </summary>
+		/// <param name="key">The key of the pair to remove.</param>
+		/// <returns>True if the key was found and the pair removed; otherwise, false.</returns>
+		/// <exception cref="System.ArgumentNullException">The key is null.</exception>
+		public bool Remove (TKey key) {
+			int index;
+			if (dictionary.TryGetValue(key, out index)) {
+				RemoveAt(index);
+				return true;
+			}
+			return false;
+		}
+
+		/// <summary>
+		/// Removes the key/value pair at the given index.
+		/// </summary>
+		/// <param name="index">The index of the key/value pair to remove.</param>
+		/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the size of the dictionary.</exception>
+		public void RemoveAt (int index) {
+			var key = keys[index];
+			for (int keyIndex = index + 1; keyIndex < keys.Count; ++keyIndex) {
+				var otherKey = keys[keyIndex];
+				dictionary[otherKey] -= 1;
+			}
+			dictionary.Remove(key);
+			keys.RemoveAt(index);
+			values.RemoveAt(index);
+			++version;
+		}
+
+		/// <summary>
+		/// Tries to get the value associated with the given key. If the key is not found,
+		/// default(TValue) value is stored in the value.
+		/// </summary>
+		/// <param name="key">The key to get the value for.</param>
+		/// <param name="value">The value used to hold the results.</param>
+		/// <returns>True if the key was found; otherwise, false.</returns>
+		/// <exception cref="System.ArgumentNullException">The key is null.</exception>
+		public bool TryGetValue (TKey key, out TValue value) {
+			int index;
+			if (dictionary.TryGetValue(key, out index)) {
+				value = values[index];
+				return true;
+			}
+			value = default(TValue);
+			return false;
+		}
+
+		/// <summary>
+		/// Gets the values in the dictionary.
+		/// </summary>
+		public ValueCollection Values {
+			get {
+				return new ValueCollection(values);
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the value at the given index.
+		/// </summary>
+		/// <param name="index">The index of the value to get.</param>
+		/// <returns>The value at the given index.</returns>
+		/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- beyond the length of the dictionary.</exception>
+		public TValue this[int index] {
+			get {
+				return values[index];
+			}
+
+			set {
+				values[index] = value;
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the value associated with the given key.
+		/// </summary>
+		/// <param name="key">The key to get the associated value by or to associate with the value.</param>
+		/// <returns>The value associated with the given key.</returns>
+		/// <exception cref="System.ArgumentNullException">The key is null.</exception>
+		/// <exception cref="System.Collections.Generic.KeyNotFoundException">The key is not in the dictionary.</exception>
+		public TValue this[TKey key] {
+			get {
+				return values[dictionary[key]];
+			}
+			set {
+				int index;
+				if (dictionary.TryGetValue(key, out index)) {
+					values[index] = value;
+				}
+				else {
+					Add(key, value);
+				}
+			}
+		}
+
+		/// <summary>
+		/// Removes all key/value pairs from the dictionary.
+		/// </summary>
+		public void Clear () {
+			dictionary.Clear();
+			keys.Clear();
+			values.Clear();
+			++version;
+		}
+
+		/// <summary>
+		/// Gets the number of key/value pairs in the dictionary.
+		/// </summary>
+		public int Count {
+			get {
+				return dictionary.Count;
+			}
+		}
+
+		/// <summary>
+		/// Gets the key/value pairs in the dictionary in the order they were added.
+		/// </summary>
+		/// <returns>An enumerator over the key/value pairs in the dictionary.</returns>
+		public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator () {
+			int startVersion = version;
+			for (int index = 0; index != keys.Count; ++index) {
+				var key = keys[index];
+				var value = values[index];
+				yield return new KeyValuePair<TKey, TValue>(key, value);
+				if (version != startVersion) {
+					throw new InvalidOperationException(CollectionModifiedMessage);
+				}
+			}
+		}
+
+		int IList<KeyValuePair<TKey, TValue>>.IndexOf (KeyValuePair<TKey, TValue> item) {
+			int index;
+			if (dictionary.TryGetValue(item.Key, out index) && Equals(values[index], item.Value)) {
+				return index;
+			}
+			return -1;
+		}
+
+		void IList<KeyValuePair<TKey, TValue>>.Insert (int index, KeyValuePair<TKey, TValue> item) {
+			Insert(index, item.Key, item.Value);
+		}
+
+		KeyValuePair<TKey, TValue> IList<KeyValuePair<TKey, TValue>>.this[int index] {
+			get {
+				TKey key = keys[index];
+				TValue value = values[index];
+				return new KeyValuePair<TKey, TValue>(key, value);
+			}
+			set {
+				TKey key = keys[index];
+				if (dictionary.Comparer.Equals(key, value.Key)) {
+					dictionary[value.Key] = index;
+				}
+				else {
+					dictionary.Add(value.Key, index);  // will throw if key already exists
+					dictionary.Remove(key);
+				}
+				keys[index] = value.Key;
+				values[index] = value.Value;
+			}
+		}
+
+		ICollection<TKey> IDictionary<TKey, TValue>.Keys {
+			get {
+				return Keys;
+			}
+		}
+
+		ICollection<TValue> IDictionary<TKey, TValue>.Values {
+			get {
+				return Values;
+			}
+		}
+
+		void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> item) {
+			Add(item.Key, item.Value);
+		}
+
+		bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> item) {
+			int index;
+			if (dictionary.TryGetValue(item.Key, out index) && Equals(values[index], item.Value)) {
+				return true;
+			}
+			return false;
+		}
+
+		void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
+			if (array == null) {
+				throw new ArgumentNullException("array");
+			}
+			if (arrayIndex < 0) {
+				throw new ArgumentOutOfRangeException("arrayIndex", arrayIndex, IndexOutOfRangeMessage);
+			}
+			for (int index = 0; index != keys.Count && arrayIndex < array.Length; ++index, ++arrayIndex) {
+				var key = keys[index];
+				var value = values[index];
+				array[arrayIndex] = new KeyValuePair<TKey, TValue>(key, value);
+			}
+		}
+
+		bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
+			get {
+				return false;
+			}
+		}
+
+		bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> item) {
+			ICollection<KeyValuePair<TKey, TValue>> self = this;
+			if (self.Contains(item)) {
+				return Remove(item.Key);
+			}
+			return false;
+		}
+
+		IEnumerator IEnumerable.GetEnumerator () {
+			return GetEnumerator();
+		}
+
+		/// <summary>
+		/// Wraps the keys in an OrderDictionary.
+		/// </summary>
+		public sealed class KeyCollection : ICollection<TKey>
+		{
+			private readonly Dictionary<TKey, int> dictionary;
+
+			/// <summary>
+			/// Initializes a new instance of a KeyCollection.
+			/// </summary>
+			/// <param name="dictionary">The OrderedDictionary whose keys to wrap.</param>
+			/// <exception cref="System.ArgumentNullException">The dictionary is null.</exception>
+			internal KeyCollection (Dictionary<TKey, int> dictionary) {
+				this.dictionary = dictionary;
+			}
+
+			/// <summary>
+			/// Copies the keys from the OrderedDictionary to the given array, starting at the given index.
+			/// </summary>
+			/// <param name="array">The array to copy the keys to.</param>
+			/// <param name="arrayIndex">The index into the array to start copying the keys.</param>
+			/// <exception cref="System.ArgumentNullException">The array is null.</exception>
+			/// <exception cref="System.ArgumentOutOfRangeException">The arrayIndex is negative.</exception>
+			/// <exception cref="System.ArgumentException">The array, starting at the given index, is not large enough to contain all the keys.</exception>
+			public void CopyTo (TKey[] array, int arrayIndex) {
+				dictionary.Keys.CopyTo(array, arrayIndex);
+			}
+
+			/// <summary>
+			/// Gets the number of keys in the OrderedDictionary.
+			/// </summary>
+			public int Count {
+				get {
+					return dictionary.Count;
+				}
+			}
+
+			/// <summary>
+			/// Gets an enumerator over the keys in the OrderedDictionary.
+			/// </summary>
+			/// <returns>The enumerator.</returns>
+			public IEnumerator<TKey> GetEnumerator () {
+				return dictionary.Keys.GetEnumerator();
+			}
+
+			[EditorBrowsable(EditorBrowsableState.Never)]
+			bool ICollection<TKey>.Contains (TKey item) {
+				return dictionary.ContainsKey(item);
+			}
+
+			[EditorBrowsable(EditorBrowsableState.Never)]
+			void ICollection<TKey>.Add (TKey item) {
+				throw new NotSupportedException(EditReadOnlyListMessage);
+			}
+
+			[EditorBrowsable(EditorBrowsableState.Never)]
+			void ICollection<TKey>.Clear () {
+				throw new NotSupportedException(EditReadOnlyListMessage);
+			}
+
+			[EditorBrowsable(EditorBrowsableState.Never)]
+			bool ICollection<TKey>.IsReadOnly {
+				get {
+					return true;
+				}
+			}
+
+			[EditorBrowsable(EditorBrowsableState.Never)]
+			bool ICollection<TKey>.Remove (TKey item) {
+				throw new NotSupportedException(EditReadOnlyListMessage);
+			}
+
+			IEnumerator IEnumerable.GetEnumerator () {
+				return GetEnumerator();
+			}
+		}
+
+		/// <summary>
+		/// Wraps the keys in an OrderDictionary.
+		/// </summary>
+		public sealed class ValueCollection : ICollection<TValue>
+		{
+			private readonly List<TValue> values;
+
+			/// <summary>
+			/// Initializes a new instance of a ValueCollection.
+			/// </summary>
+			/// <param name="values">The OrderedDictionary whose keys to wrap.</param>
+			/// <exception cref="System.ArgumentNullException">The dictionary is null.</exception>
+			internal ValueCollection (List<TValue> values) {
+				this.values = values;
+			}
+
+			/// <summary>
+			/// Copies the values from the OrderedDictionary to the given array, starting at the given index.
+			/// </summary>
+			/// <param name="array">The array to copy the values to.</param>
+			/// <param name="arrayIndex">The index into the array to start copying the values.</param>
+			/// <exception cref="System.ArgumentNullException">The array is null.</exception>
+			/// <exception cref="System.ArgumentOutOfRangeException">The arrayIndex is negative.</exception>
+			/// <exception cref="System.ArgumentException">The array, starting at the given index, is not large enough to contain all the values.</exception>
+			public void CopyTo (TValue[] array, int arrayIndex) {
+				values.CopyTo(array, arrayIndex);
+			}
+
+			/// <summary>
+			/// Gets the number of values in the OrderedDictionary.
+			/// </summary>
+			public int Count {
+				get {
+					return values.Count;
+				}
+			}
+
+			/// <summary>
+			/// Gets an enumerator over the values in the OrderedDictionary.
+			/// </summary>
+			/// <returns>The enumerator.</returns>
+			public IEnumerator<TValue> GetEnumerator () {
+				return values.GetEnumerator();
+			}
+
+			[EditorBrowsable(EditorBrowsableState.Never)]
+			bool ICollection<TValue>.Contains (TValue item) {
+				return values.Contains(item);
+			}
+
+			[EditorBrowsable(EditorBrowsableState.Never)]
+			void ICollection<TValue>.Add (TValue item) {
+				throw new NotSupportedException(EditReadOnlyListMessage);
+			}
+
+			[EditorBrowsable(EditorBrowsableState.Never)]
+			void ICollection<TValue>.Clear () {
+				throw new NotSupportedException(EditReadOnlyListMessage);
+			}
+
+			[EditorBrowsable(EditorBrowsableState.Never)]
+			bool ICollection<TValue>.IsReadOnly {
+				get {
+					return true;
+				}
+			}
+
+			[EditorBrowsable(EditorBrowsableState.Never)]
+			bool ICollection<TValue>.Remove (TValue item) {
+				throw new NotSupportedException(EditReadOnlyListMessage);
+			}
+
+			IEnumerator IEnumerable.GetEnumerator () {
+				return GetEnumerator();
+			}
+		}
+	}
+
+	internal class OrderedDictionaryDebugView<TKey, TValue>
+	{
+		private readonly OrderedDictionary<TKey, TValue> dictionary;
+
+		public OrderedDictionaryDebugView (OrderedDictionary<TKey, TValue> dictionary) {
+			this.dictionary = dictionary;
+		}
+
+		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+		public KeyValuePair<TKey, TValue>[] Items {
+			get {
+				return dictionary.ToArray();
+			}
+		}
+	}
+
+	/// <summary>
+	/// Provides extensions methods for constructing instances of <see cref="OrderedDictionary{TKey, TValue}"/>.
+	/// </summary>
+	public static class CollectionExtensions
+	{
+		#region ToOrderedDictionary
+
+		/// <summary>
+		/// Creates a new OrderedDictionary from the given collection, using the key selector to extract the key.
+		/// </summary>
+		/// <typeparam name="TSource">The type of the items in the collection.</typeparam>
+		/// <typeparam name="TKey">The type of the key.</typeparam>
+		/// <param name="source">The items to created the OrderedDictionary from.</param>
+		/// <param name="keySelector">A delegate that can extract a key from an item in the collection.</param>
+		/// <returns>An OrderedDictionary mapping the extracted keys to their values.</returns>
+		public static OrderedDictionary<TKey, TSource> ToOrderedDictionary<TSource, TKey> (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) {
+			return ToOrderedDictionary(source, keySelector, null);
+		}
+
+		/// <summary>
+		/// Creates a new OrderedDictionary from the given collection, using the key selector to extract the key.
+		/// The key comparer is passed to the OrderedDictionary for comparing the extracted keys.
+		/// </summary>
+		/// <typeparam name="TSource">The type of the items in the collection.</typeparam>
+		/// <typeparam name="TKey">The type of the key.</typeparam>
+		/// <param name="source">The items to created the OrderedDictionary from.</param>
+		/// <param name="keySelector">A delegate that can extract a key from an item in the collection.</param>
+		/// <param name="comparer">The key equality comparer to use to compare keys in the dictionary.</param>
+		/// <returns>An OrderedDictionary mapping the extracted keys to their values.</returns>
+		public static OrderedDictionary<TKey, TSource> ToOrderedDictionary<TSource, TKey> (
+			this IEnumerable<TSource> source,
+			Func<TSource, TKey> keySelector,
+			IEqualityComparer<TKey> comparer) {
+			if (source == null) {
+				throw new ArgumentNullException("source");
+			}
+			if (keySelector == null) {
+				throw new ArgumentNullException("keySelector");
+			}
+			var dictionary = new OrderedDictionary<TKey, TSource>(comparer);
+			foreach (TSource item in source) {
+				TKey key = keySelector(item);
+				dictionary.Add(key, item);
+			}
+			return dictionary;
+		}
+
+		#endregion
+	}
+}

+ 1 - 1
spine-csharp/src/SkeletonBinary.cs

@@ -328,7 +328,7 @@ namespace Spine {
 			if (defaultSkin) {
 			if (defaultSkin) {
 				slotCount = input.ReadInt(true);
 				slotCount = input.ReadInt(true);
 				if (slotCount == 0) return null;
 				if (slotCount == 0) return null;
-				skin = new Skin("default"));
+				skin = new Skin("default");
 			} else {
 			} else {
 				skin = new Skin(input.ReadStringRef());
 				skin = new Skin(input.ReadStringRef());
 				Object[] bones = skin.bones.Resize(input.ReadInt(true)).Items;
 				Object[] bones = skin.bones.Resize(input.ReadInt(true)).Items;

+ 11 - 16
spine-csharp/src/Skin.cs

@@ -30,7 +30,7 @@
 using System;
 using System;
 using System.Collections;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Collections.Specialized;
+using Spine.Collections;
 
 
 namespace Spine {
 namespace Spine {
 	/// <summary>Stores attachments by slot index and attachment name.
 	/// <summary>Stores attachments by slot index and attachment name.
@@ -39,12 +39,12 @@ namespace Spine {
 	/// </summary>
 	/// </summary>
 	public class Skin {
 	public class Skin {
 		internal string name;
 		internal string name;
-		private OrderedDictionary attachments = new OrderedDictionary(SkinEntryComparer.Instance); // contains <SkinEntry, Attachment>
+		private OrderedDictionary<SkinEntry, Attachment> attachments = new OrderedDictionary<SkinEntry, Attachment>(SkinEntryComparer.Instance);
 		internal readonly ExposedList<BoneData> bones = new ExposedList<BoneData>();
 		internal readonly ExposedList<BoneData> bones = new ExposedList<BoneData>();
 		internal readonly ExposedList<ConstraintData> constraints = new ExposedList<ConstraintData>();
 		internal readonly ExposedList<ConstraintData> constraints = new ExposedList<ConstraintData>();
 
 
 		public string Name { get { return name; } }
 		public string Name { get { return name; } }
-		public OrderedDictionary Attachments { get { return attachments; } }
+		public OrderedDictionary<SkinEntry, Attachment> Attachments { get { return attachments; } }
 		public ExposedList<BoneData> Bones { get { return bones; } }
 		public ExposedList<BoneData> Bones { get { return bones; } }
 		public ExposedList<ConstraintData> Constraints { get { return constraints; } }
 		public ExposedList<ConstraintData> Constraints { get { return constraints; } }
 		
 		
@@ -94,8 +94,9 @@ namespace Spine {
 		/// <returns>May be null.</returns>
 		/// <returns>May be null.</returns>
 		public Attachment GetAttachment (int slotIndex, string name) {
 		public Attachment GetAttachment (int slotIndex, string name) {
 			var lookup = new SkinEntry(slotIndex, name, null);
 			var lookup = new SkinEntry(slotIndex, name, null);
-			var obj = attachments[lookup];
-			return (obj == null) ? null : (Attachment)obj;
+			Attachment attachment = null;
+			bool containsKey = attachments.TryGetValue(lookup, out attachment);
+			return containsKey ? attachment : null;
 		}
 		}
 
 
 		/// <summary> Removes the attachment in the skin for the specified slot index and name, if any.</summary>
 		/// <summary> Removes the attachment in the skin for the specified slot index and name, if any.</summary>
@@ -106,11 +107,8 @@ namespace Spine {
 		}
 		}
 
 
 		///<summary>Returns all attachments contained in this skin.</summary>
 		///<summary>Returns all attachments contained in this skin.</summary>
-		public List<SkinEntry> GetAttachments () {
-			List<SkinEntry> entries = new List<SkinEntry>();
-			foreach (SkinEntry entry in this.attachments.Keys)
-				entries.Add(entry);
-			return entries;
+		public ICollection<SkinEntry> GetAttachments () {
+			return this.attachments.Keys;
 		}
 		}
 
 
 		/// <summary>Returns all attachments in this skin for the specified slot index.</summary>
 		/// <summary>Returns all attachments in this skin for the specified slot index.</summary>
@@ -178,19 +176,16 @@ namespace Spine {
 		}
 		}
 	
 	
 		// Avoids boxing in the dictionary and is necessary to omit entry.attachment in the comparison.
 		// Avoids boxing in the dictionary and is necessary to omit entry.attachment in the comparison.
-		class SkinEntryComparer : IEqualityComparer {
+		class SkinEntryComparer : IEqualityComparer<SkinEntry> {
 			internal static readonly SkinEntryComparer Instance = new SkinEntryComparer();
 			internal static readonly SkinEntryComparer Instance = new SkinEntryComparer();
 
 
-			bool IEqualityComparer.Equals (object o1, object o2) {
-				var e1 = (SkinEntry)o1;
-				var e2 = (SkinEntry)o2;
+			bool IEqualityComparer<SkinEntry>.Equals (SkinEntry e1, SkinEntry e2) {
 				if (e1.SlotIndex != e2.SlotIndex) return false;
 				if (e1.SlotIndex != e2.SlotIndex) return false;
 				if (!string.Equals(e1.Name, e2.Name, StringComparison.Ordinal)) return false;
 				if (!string.Equals(e1.Name, e2.Name, StringComparison.Ordinal)) return false;
 				return true;
 				return true;
 			}
 			}
 
 
-			int IEqualityComparer.GetHashCode (object o) {
-				var e = (SkinEntry)o;
+			int IEqualityComparer<SkinEntry>.GetHashCode (SkinEntry e) {
 				return e.Name.GetHashCode() + e.SlotIndex * 37;
 				return e.Name.GetHashCode() + e.SlotIndex * 37;
 			}
 			}
 		}
 		}

+ 11 - 28
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SkeletonDataAssetInspector.cs

@@ -484,49 +484,32 @@ namespace Spine.Unity.Editor {
 					}
 					}
 				}
 				}
 
 
-				var slotAttachments = new List<Attachment>();
-				var slotAttachmentNames = new List<string>();
-				var defaultSkinAttachmentNames = new List<string>();
+				var slotAttachments = new List<Skin.SkinEntry>();
+				var defaultSkinAttachments = new List<Skin.SkinEntry>();
 				var slotsItems = preview.Skeleton.Slots.Items;
 				var slotsItems = preview.Skeleton.Slots.Items;
 				for (int i = preview.Skeleton.Slots.Count - 1; i >= 0; i--) {
 				for (int i = preview.Skeleton.Slots.Count - 1; i >= 0; i--) {
 					Slot slot = slotsItems[i];
 					Slot slot = slotsItems[i];
 					EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot));
 					EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot));
 					if (showAttachments) {
 					if (showAttachments) {
 						slotAttachments.Clear();
 						slotAttachments.Clear();
-						slotAttachmentNames.Clear();
-						defaultSkinAttachmentNames.Clear();
+						defaultSkinAttachments.Clear();
 
 
 						using (new SpineInspectorUtility.IndentScope()) {
 						using (new SpineInspectorUtility.IndentScope()) {
 							{
 							{
-								var skinEntries = new List<Skin.SkinEntry>();
-								skin.GetAttachments(i, skinEntries);
-								foreach (var entry in skinEntries) {
-									slotAttachments.Add(entry.Attachment);
-									slotAttachmentNames.Add(entry.Name);
-								}
-
+								skin.GetAttachments(i, slotAttachments);
 								if (skin != defaultSkin) {
 								if (skin != defaultSkin) {
-									skinEntries.Clear();
-									defaultSkin.GetAttachments(i, skinEntries);
-									foreach (var entry in skinEntries) {
-										slotAttachments.Add(entry.Attachment);
-										slotAttachmentNames.Add(entry.Name);
-										defaultSkinAttachmentNames.Add(entry.Name);
-									}
-
+									defaultSkin.GetAttachments(i, slotAttachments);
+									defaultSkin.GetAttachments(i, defaultSkinAttachments);
 								} else {
 								} else {
-									skinEntries.Clear();
-									defaultSkin.GetAttachments(i, skinEntries);
-									foreach (var entry in skinEntries) {
-										defaultSkinAttachmentNames.Add(entry.Name);
-									}
+									defaultSkin.GetAttachments(i, defaultSkinAttachments);
 								}
 								}
 							}
 							}
 
 
 							for (int a = 0; a < slotAttachments.Count; a++) {
 							for (int a = 0; a < slotAttachments.Count; a++) {
-								Attachment attachment = slotAttachments[a];
-								string attachmentName = slotAttachmentNames[a];
-								bool attachmentIsFromSkin = !defaultSkinAttachmentNames.Contains(attachmentName);
+								var skinEntry = slotAttachments[a];
+								Attachment attachment = skinEntry.Attachment;
+								string attachmentName = skinEntry.Name;
+								bool attachmentIsFromSkin = !defaultSkinAttachments.Contains(skinEntry);
 
 
 								Texture2D attachmentTypeIcon = Icons.GetAttachmentIcon(attachment);
 								Texture2D attachmentTypeIcon = Icons.GetAttachmentIcon(attachment);
 								bool initialState = slot.Attachment == attachment;
 								bool initialState = slot.Attachment == attachment;

+ 1 - 1
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/PointFollowerInspector.cs

@@ -111,7 +111,7 @@ namespace Spine.Unity.Editor {
 		}
 		}
 
 
 		static void DrawPointsInSkin (Skin skin, Skeleton skeleton, Transform transform) {
 		static void DrawPointsInSkin (Skin skin, Skeleton skeleton, Transform transform) {
-			foreach (DictionaryEntry skinEntry in skin.Attachments) {
+			foreach (var skinEntry in skin.Attachments) {
 				var attachment = skinEntry.Value as PointAttachment;
 				var attachment = skinEntry.Value as PointAttachment;
 				if (attachment != null) {
 				if (attachment != null) {
 					var skinKey = (Skin.SkinEntry)skinEntry.Key;
 					var skinKey = (Skin.SkinEntry)skinEntry.Key;

+ 4 - 8
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonUtilityBoneInspector.cs

@@ -80,17 +80,13 @@ namespace Spine.Unity.Editor {
 			for(int i = 0; i < slotCount; i++){
 			for(int i = 0; i < slotCount; i++){
 				Slot slot = skeletonUtility.skeletonRenderer.skeleton.Slots.Items[i];
 				Slot slot = skeletonUtility.skeletonRenderer.skeleton.Slots.Items[i];
 				if (slot.Bone == utilityBone.bone) {
 				if (slot.Bone == utilityBone.bone) {
-					var slotAttachments = new List<Attachment>();
-					var skinEntries = new List<Skin.SkinEntry>();
+					var slotAttachments = new List<Skin.SkinEntry>();
 					int slotIndex = skeleton.FindSlotIndex(slot.Data.Name);
 					int slotIndex = skeleton.FindSlotIndex(slot.Data.Name);
-					skin.GetAttachments(slotIndex, skinEntries);
-					foreach (var entry in skinEntries) {
-						slotAttachments.Add(entry.Attachment);
-					}
-
+					skin.GetAttachments(slotIndex, slotAttachments);
+					
 					var boundingBoxes = new List<BoundingBoxAttachment>();
 					var boundingBoxes = new List<BoundingBoxAttachment>();
 					foreach (var att in slotAttachments) {
 					foreach (var att in slotAttachments) {
-						var boundingBoxAttachment = att as BoundingBoxAttachment;
+						var boundingBoxAttachment = att.Attachment as BoundingBoxAttachment;
 						if (boundingBoxAttachment != null)
 						if (boundingBoxAttachment != null)
 							boundingBoxes.Add(boundingBoxAttachment);
 							boundingBoxes.Add(boundingBoxAttachment);
 					}
 					}

+ 0 - 8
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs

@@ -39,14 +39,6 @@
 #define NEW_PREFAB_SYSTEM
 #define NEW_PREFAB_SYSTEM
 #endif
 #endif
 
 
-#if UNITY_2018 || UNITY_2019 || UNITY_2018_3_OR_NEWER
-#define NEWHIERARCHYWINDOWCALLBACKS
-#endif
-
-#if UNITY_2018_3_OR_NEWER
-#define NEW_PREFERENCES_SETTINGS_PROVIDER
-#endif
-
 using UnityEngine;
 using UnityEngine;
 using UnityEditor;
 using UnityEditor;
 using System.Collections.Generic;
 using System.Collections.Generic;

+ 0 - 4
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/BuildSettings.cs

@@ -43,10 +43,6 @@
 #define NEWHIERARCHYWINDOWCALLBACKS
 #define NEWHIERARCHYWINDOWCALLBACKS
 #endif
 #endif
 
 
-#if UNITY_2018_3_OR_NEWER
-#define NEW_PREFERENCES_SETTINGS_PROVIDER
-#endif
-
 using UnityEngine;
 using UnityEngine;
 using UnityEditor;
 using UnityEditor;
 using System.Collections.Generic;
 using System.Collections.Generic;

+ 0 - 12
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/DataReloadHandler.cs

@@ -35,18 +35,6 @@
 #define NEWPLAYMODECALLBACKS
 #define NEWPLAYMODECALLBACKS
 #endif
 #endif
 
 
-#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
-#define NEW_PREFAB_SYSTEM
-#endif
-
-#if UNITY_2018 || UNITY_2019 || UNITY_2018_3_OR_NEWER
-#define NEWHIERARCHYWINDOWCALLBACKS
-#endif
-
-#if UNITY_2018_3_OR_NEWER
-#define NEW_PREFERENCES_SETTINGS_PROVIDER
-#endif
-
 using UnityEngine;
 using UnityEngine;
 using UnityEditor;
 using UnityEditor;
 using System.Collections.Generic;
 using System.Collections.Generic;

+ 0 - 16
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/Icons.cs

@@ -31,22 +31,6 @@
 
 
 #define SPINE_SKELETONMECANIM
 #define SPINE_SKELETONMECANIM
 
 
-#if UNITY_2017_2_OR_NEWER
-#define NEWPLAYMODECALLBACKS
-#endif
-
-#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
-#define NEW_PREFAB_SYSTEM
-#endif
-
-#if UNITY_2018 || UNITY_2019 || UNITY_2018_3_OR_NEWER
-#define NEWHIERARCHYWINDOWCALLBACKS
-#endif
-
-#if UNITY_2018_3_OR_NEWER
-#define NEW_PREFERENCES_SETTINGS_PROVIDER
-#endif
-
 using UnityEngine;
 using UnityEngine;
 using UnityEditor;
 using UnityEditor;
 using System.Collections.Generic;
 using System.Collections.Generic;

+ 0 - 16
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/Instantiation.cs

@@ -31,22 +31,6 @@
 
 
 #define SPINE_SKELETONMECANIM
 #define SPINE_SKELETONMECANIM
 
 
-#if UNITY_2017_2_OR_NEWER
-#define NEWPLAYMODECALLBACKS
-#endif
-
-#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
-#define NEW_PREFAB_SYSTEM
-#endif
-
-#if UNITY_2018 || UNITY_2019 || UNITY_2018_3_OR_NEWER
-#define NEWHIERARCHYWINDOWCALLBACKS
-#endif
-
-#if UNITY_2018_3_OR_NEWER
-#define NEW_PREFERENCES_SETTINGS_PROVIDER
-#endif
-
 using UnityEngine;
 using UnityEngine;
 using UnityEditor;
 using UnityEditor;
 using System.Collections.Generic;
 using System.Collections.Generic;

+ 15 - 7
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/Preferences.cs

@@ -39,10 +39,6 @@
 #define NEW_PREFAB_SYSTEM
 #define NEW_PREFAB_SYSTEM
 #endif
 #endif
 
 
-#if UNITY_2018 || UNITY_2019 || UNITY_2018_3_OR_NEWER
-#define NEWHIERARCHYWINDOWCALLBACKS
-#endif
-
 #if UNITY_2018_3_OR_NEWER
 #if UNITY_2018_3_OR_NEWER
 #define NEW_PREFERENCES_SETTINGS_PROVIDER
 #define NEW_PREFERENCES_SETTINGS_PROVIDER
 #endif
 #endif
@@ -139,6 +135,9 @@ namespace Spine.Unity.Editor {
 			const string AUTO_RELOAD_SCENESKELETONS_KEY = "SPINE_AUTO_RELOAD_SCENESKELETONS";
 			const string AUTO_RELOAD_SCENESKELETONS_KEY = "SPINE_AUTO_RELOAD_SCENESKELETONS";
 			public static bool autoReloadSceneSkeletons = SpinePreferences.DEFAULT_AUTO_RELOAD_SCENESKELETONS;
 			public static bool autoReloadSceneSkeletons = SpinePreferences.DEFAULT_AUTO_RELOAD_SCENESKELETONS;
 
 
+			const string MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY = "SPINE_MECANIM_EVENT_INCLUDE_FOLDERNAME";
+			public static bool mecanimEventIncludeFolderName = SpinePreferences.DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME;
+
 			static bool preferencesLoaded = false;
 			static bool preferencesLoaded = false;
 
 
 			public static void Load () {
 			public static void Load () {
@@ -152,6 +151,7 @@ namespace Spine.Unity.Editor {
 				showHierarchyIcons = EditorPrefs.GetBool(SHOW_HIERARCHY_ICONS_KEY, SpinePreferences.DEFAULT_SHOW_HIERARCHY_ICONS);
 				showHierarchyIcons = EditorPrefs.GetBool(SHOW_HIERARCHY_ICONS_KEY, SpinePreferences.DEFAULT_SHOW_HIERARCHY_ICONS);
 				setTextureImporterSettings = EditorPrefs.GetBool(SET_TEXTUREIMPORTER_SETTINGS_KEY, SpinePreferences.DEFAULT_SET_TEXTUREIMPORTER_SETTINGS);
 				setTextureImporterSettings = EditorPrefs.GetBool(SET_TEXTUREIMPORTER_SETTINGS_KEY, SpinePreferences.DEFAULT_SET_TEXTUREIMPORTER_SETTINGS);
 				autoReloadSceneSkeletons = EditorPrefs.GetBool(AUTO_RELOAD_SCENESKELETONS_KEY, SpinePreferences.DEFAULT_AUTO_RELOAD_SCENESKELETONS);
 				autoReloadSceneSkeletons = EditorPrefs.GetBool(AUTO_RELOAD_SCENESKELETONS_KEY, SpinePreferences.DEFAULT_AUTO_RELOAD_SCENESKELETONS);
+				mecanimEventIncludeFolderName = EditorPrefs.GetBool(MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY, SpinePreferences.DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME);
 				atlasTxtImportWarning = EditorPrefs.GetBool(ATLASTXT_WARNING_KEY, SpinePreferences.DEFAULT_ATLASTXT_WARNING);
 				atlasTxtImportWarning = EditorPrefs.GetBool(ATLASTXT_WARNING_KEY, SpinePreferences.DEFAULT_ATLASTXT_WARNING);
 				textureImporterWarning = EditorPrefs.GetBool(TEXTUREIMPORTER_WARNING_KEY, SpinePreferences.DEFAULT_TEXTUREIMPORTER_WARNING);
 				textureImporterWarning = EditorPrefs.GetBool(TEXTUREIMPORTER_WARNING_KEY, SpinePreferences.DEFAULT_TEXTUREIMPORTER_WARNING);
 
 
@@ -159,7 +159,7 @@ namespace Spine.Unity.Editor {
 				preferencesLoaded = true;
 				preferencesLoaded = true;
 			}
 			}
 
 
-		#if NEW_PREFERENCES_SETTINGS_PROVIDER
+#if NEW_PREFERENCES_SETTINGS_PROVIDER
 			public static void CopyOldToNewPreferences(ref SpinePreferences newPreferences) {
 			public static void CopyOldToNewPreferences(ref SpinePreferences newPreferences) {
 				newPreferences.defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, SpinePreferences.DEFAULT_DEFAULT_MIX);
 				newPreferences.defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, SpinePreferences.DEFAULT_DEFAULT_MIX);
 				newPreferences.defaultScale = EditorPrefs.GetFloat(DEFAULT_SCALE_KEY, SpinePreferences.DEFAULT_DEFAULT_SCALE);
 				newPreferences.defaultScale = EditorPrefs.GetFloat(DEFAULT_SCALE_KEY, SpinePreferences.DEFAULT_DEFAULT_SCALE);
@@ -168,6 +168,7 @@ namespace Spine.Unity.Editor {
 				newPreferences.showHierarchyIcons = EditorPrefs.GetBool(SHOW_HIERARCHY_ICONS_KEY, SpinePreferences.DEFAULT_SHOW_HIERARCHY_ICONS);
 				newPreferences.showHierarchyIcons = EditorPrefs.GetBool(SHOW_HIERARCHY_ICONS_KEY, SpinePreferences.DEFAULT_SHOW_HIERARCHY_ICONS);
 				newPreferences.setTextureImporterSettings = EditorPrefs.GetBool(SET_TEXTUREIMPORTER_SETTINGS_KEY, SpinePreferences.DEFAULT_SET_TEXTUREIMPORTER_SETTINGS);
 				newPreferences.setTextureImporterSettings = EditorPrefs.GetBool(SET_TEXTUREIMPORTER_SETTINGS_KEY, SpinePreferences.DEFAULT_SET_TEXTUREIMPORTER_SETTINGS);
 				newPreferences.autoReloadSceneSkeletons = EditorPrefs.GetBool(AUTO_RELOAD_SCENESKELETONS_KEY, SpinePreferences.DEFAULT_AUTO_RELOAD_SCENESKELETONS);
 				newPreferences.autoReloadSceneSkeletons = EditorPrefs.GetBool(AUTO_RELOAD_SCENESKELETONS_KEY, SpinePreferences.DEFAULT_AUTO_RELOAD_SCENESKELETONS);
+				newPreferences.mecanimEventIncludeFolderName = EditorPrefs.GetBool(MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY, SpinePreferences.DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME);
 				newPreferences.atlasTxtImportWarning = EditorPrefs.GetBool(ATLASTXT_WARNING_KEY, SpinePreferences.DEFAULT_ATLASTXT_WARNING);
 				newPreferences.atlasTxtImportWarning = EditorPrefs.GetBool(ATLASTXT_WARNING_KEY, SpinePreferences.DEFAULT_ATLASTXT_WARNING);
 				newPreferences.textureImporterWarning = EditorPrefs.GetBool(TEXTUREIMPORTER_WARNING_KEY, SpinePreferences.DEFAULT_TEXTUREIMPORTER_WARNING);
 				newPreferences.textureImporterWarning = EditorPrefs.GetBool(TEXTUREIMPORTER_WARNING_KEY, SpinePreferences.DEFAULT_TEXTUREIMPORTER_WARNING);
 			}
 			}
@@ -180,12 +181,13 @@ namespace Spine.Unity.Editor {
 				EditorPrefs.SetBool(SHOW_HIERARCHY_ICONS_KEY, preferences.showHierarchyIcons);
 				EditorPrefs.SetBool(SHOW_HIERARCHY_ICONS_KEY, preferences.showHierarchyIcons);
 				EditorPrefs.SetBool(SET_TEXTUREIMPORTER_SETTINGS_KEY, preferences.setTextureImporterSettings);
 				EditorPrefs.SetBool(SET_TEXTUREIMPORTER_SETTINGS_KEY, preferences.setTextureImporterSettings);
 				EditorPrefs.SetBool(AUTO_RELOAD_SCENESKELETONS_KEY, preferences.autoReloadSceneSkeletons);
 				EditorPrefs.SetBool(AUTO_RELOAD_SCENESKELETONS_KEY, preferences.autoReloadSceneSkeletons);
+				EditorPrefs.SetBool(MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY, preferences.mecanimEventIncludeFolderName);
 				EditorPrefs.SetBool(ATLASTXT_WARNING_KEY, preferences.atlasTxtImportWarning);
 				EditorPrefs.SetBool(ATLASTXT_WARNING_KEY, preferences.atlasTxtImportWarning);
 				EditorPrefs.SetBool(TEXTUREIMPORTER_WARNING_KEY, preferences.textureImporterWarning);
 				EditorPrefs.SetBool(TEXTUREIMPORTER_WARNING_KEY, preferences.textureImporterWarning);
 			}
 			}
-		#endif
+#endif
 
 
-		#if !NEW_PREFERENCES_SETTINGS_PROVIDER
+#if !NEW_PREFERENCES_SETTINGS_PROVIDER
 			public static void HandlePreferencesGUI () {
 			public static void HandlePreferencesGUI () {
 				if (!preferencesLoaded)
 				if (!preferencesLoaded)
 					Load();
 					Load();
@@ -236,6 +238,12 @@ namespace Spine.Unity.Editor {
 					SpineEditorUtilities.BoolPrefsField(ref defaultInstantiateLoop, DEFAULT_INSTANTIATE_LOOP_KEY, new GUIContent("Default Loop", "Spawn Spine GameObjects with loop enabled."));
 					SpineEditorUtilities.BoolPrefsField(ref defaultInstantiateLoop, DEFAULT_INSTANTIATE_LOOP_KEY, new GUIContent("Default Loop", "Spawn Spine GameObjects with loop enabled."));
 				}
 				}
 
 
+				EditorGUILayout.Space();
+				EditorGUILayout.LabelField("Mecanim Bake Settings", EditorStyles.boldLabel);
+				{
+					SpineEditorUtilities.BoolPrefsField(ref mecanimEventIncludeFolderName, MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY, new GUIContent("Include Folder Name in Event", "When enabled, Mecanim events will call methods named 'FolderNameEventName', when disabled it will call 'EventName'."));
+				}
+
 				EditorGUILayout.Space();
 				EditorGUILayout.Space();
 				EditorGUILayout.LabelField("Handles and Gizmos", EditorStyles.boldLabel);
 				EditorGUILayout.LabelField("Handles and Gizmos", EditorStyles.boldLabel);
 				{
 				{

+ 0 - 4
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineEditorUtilities.cs

@@ -38,10 +38,6 @@
 #define NEWPLAYMODECALLBACKS
 #define NEWPLAYMODECALLBACKS
 #endif
 #endif
 
 
-#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
-#define NEW_PREFAB_SYSTEM
-#endif
-
 #if UNITY_2018 || UNITY_2019 || UNITY_2018_3_OR_NEWER
 #if UNITY_2018 || UNITY_2019 || UNITY_2018_3_OR_NEWER
 #define NEWHIERARCHYWINDOWCALLBACKS
 #define NEWHIERARCHYWINDOWCALLBACKS
 #endif
 #endif

+ 0 - 16
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineHandles.cs

@@ -31,22 +31,6 @@
 
 
 #define SPINE_SKELETONMECANIM
 #define SPINE_SKELETONMECANIM
 
 
-#if UNITY_2017_2_OR_NEWER
-#define NEWPLAYMODECALLBACKS
-#endif
-
-#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
-#define NEW_PREFAB_SYSTEM
-#endif
-
-#if UNITY_2018 || UNITY_2019 || UNITY_2018_3_OR_NEWER
-#define NEWHIERARCHYWINDOWCALLBACKS
-#endif
-
-#if UNITY_2018_3_OR_NEWER
-#define NEW_PREFERENCES_SETTINGS_PROVIDER
-#endif
-
 using UnityEngine;
 using UnityEngine;
 using UnityEditor;
 using UnityEditor;
 using System.Collections.Generic;
 using System.Collections.Generic;

+ 10 - 19
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonBaker.cs

@@ -339,28 +339,14 @@ namespace Spine.Unity.Editor {
 					slotTransform.parent = prefabRoot.transform;
 					slotTransform.parent = prefabRoot.transform;
 					slotTable.Add(slotData.Name, slotTransform);
 					slotTable.Add(slotData.Name, slotTransform);
 
 
-					List<Attachment> attachments = new List<Attachment>();
-					List<string> attachmentNames = new List<string>();
-
 					var skinEntries = new List<Skin.SkinEntry>();
 					var skinEntries = new List<Skin.SkinEntry>();
 					skin.GetAttachments(slotIndex, skinEntries);
 					skin.GetAttachments(slotIndex, skinEntries);
-					foreach (var entry in skinEntries) {
-						attachments.Add(entry.Attachment);
-						attachmentNames.Add(entry.Name);
-					}
-
-					if (skin != skeletonData.DefaultSkin) {
-						skinEntries.Clear();
+					if (skin != skeletonData.DefaultSkin)
 						skeletonData.DefaultSkin.GetAttachments(slotIndex, skinEntries);
 						skeletonData.DefaultSkin.GetAttachments(slotIndex, skinEntries);
-						foreach (var entry in skinEntries) {
-							attachments.Add(entry.Attachment);
-							attachmentNames.Add(entry.Name);
-						}
-					}
 
 
-					for (int a = 0; a < attachments.Count; a++) {
-						var attachment = attachments[a];
-						string attachmentName = attachmentNames[a];
+					for (int a = 0; a < skinEntries.Count; a++) {
+						var attachment = skinEntries[a].Attachment;
+						string attachmentName = skinEntries[a].Name;
 						string attachmentMeshName = "[" + slotData.Name + "] " + attachmentName;
 						string attachmentMeshName = "[" + slotData.Name + "] " + attachmentName;
 						Vector3 offset = Vector3.zero;
 						Vector3 offset = Vector3.zero;
 						float rotation = 0;
 						float rotation = 0;
@@ -1315,9 +1301,14 @@ namespace Spine.Unity.Editor {
 			var animEvents = new List<AnimationEvent>();
 			var animEvents = new List<AnimationEvent>();
 			for (int i = 0, n = frames.Length; i < n; i++) {
 			for (int i = 0, n = frames.Length; i < n; i++) {
 				var spineEvent = events[i];
 				var spineEvent = events[i];
+				string eventName = spineEvent.Data.Name;
+				if (SpineEditorUtilities.Preferences.mecanimEventIncludeFolderName)
+					eventName = eventName.Replace("/", ""); // calls method FolderNameEventName()
+				else
+					eventName = eventName.Substring(eventName.LastIndexOf('/') + 1); // calls method EventName()
 				var unityAnimationEvent = new AnimationEvent {
 				var unityAnimationEvent = new AnimationEvent {
 					time = frames[i],
 					time = frames[i],
-					functionName = spineEvent.Data.Name,
+					functionName = eventName,
 					messageOptions = eventOptions
 					messageOptions = eventOptions
 				};
 				};
 
 

+ 8 - 16
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonDebugWindow.cs

@@ -84,7 +84,7 @@ namespace Spine.Unity.Editor {
 		[SpineBone(dataField:"skeletonRenderer")]
 		[SpineBone(dataField:"skeletonRenderer")]
 		public string boneName;
 		public string boneName;
 
 
-		readonly Dictionary<Slot, List<Attachment>> attachmentTable = new Dictionary<Slot, List<Attachment>>();
+		readonly Dictionary<Slot, List<Skin.SkinEntry>> attachmentTable = new Dictionary<Slot, List<Skin.SkinEntry>>();
 
 
 		static bool staticLostValues = true;
 		static bool staticLostValues = true;
 
 
@@ -338,7 +338,7 @@ namespace Spine.Unity.Editor {
 						}
 						}
 
 
 						int baseIndent = EditorGUI.indentLevel;
 						int baseIndent = EditorGUI.indentLevel;
-						foreach (KeyValuePair<Slot, List<Attachment>> pair in attachmentTable) {
+						foreach (KeyValuePair<Slot, List<Skin.SkinEntry>> pair in attachmentTable) {
 							Slot slot = pair.Key;
 							Slot slot = pair.Key;
 
 
 							using (new EditorGUILayout.HorizontalScope()) {
 							using (new EditorGUILayout.HorizontalScope()) {
@@ -352,7 +352,8 @@ namespace Spine.Unity.Editor {
 								}
 								}
 							}
 							}
 
 
-							foreach (var attachment in pair.Value) {
+							foreach (var skinEntry in pair.Value) {
+								var attachment = skinEntry.Attachment;
 								GUI.contentColor = slot.Attachment == attachment ? Color.white : Color.grey;
 								GUI.contentColor = slot.Attachment == attachment ? Color.white : Color.grey;
 								EditorGUI.indentLevel = baseIndent + 2;
 								EditorGUI.indentLevel = baseIndent + 2;
 								var icon = Icons.GetAttachmentIcon(attachment);
 								var icon = Icons.GetAttachmentIcon(attachment);
@@ -577,21 +578,12 @@ namespace Spine.Unity.Editor {
 
 
 			attachmentTable.Clear();
 			attachmentTable.Clear();
 			for (int i = skeleton.Slots.Count - 1; i >= 0; i--) {
 			for (int i = skeleton.Slots.Count - 1; i >= 0; i--) {
-				var attachments = new List<Attachment>();
+				var attachments = new List<Skin.SkinEntry>();
 				attachmentTable.Add(skeleton.Slots.Items[i], attachments);
 				attachmentTable.Add(skeleton.Slots.Items[i], attachments);
 				// Add skin attachments.
 				// Add skin attachments.
-				var skinEntries = new List<Skin.SkinEntry>();
-				skin.GetAttachments(i, skinEntries);
-				foreach (var entry in skinEntries) {
-					attachments.Add(entry.Attachment);
-				}
-				if (notDefaultSkin) { // Add default skin attachments.
-					skinEntries.Clear();
-					defaultSkin.GetAttachments(i, skinEntries);
-					foreach (var entry in skinEntries) {
-						attachments.Add(entry.Attachment);
-					}
-				}
+				skin.GetAttachments(i, attachments);
+				if (notDefaultSkin) // Add default skin attachments.
+					defaultSkin.GetAttachments(i, attachments);
 			}
 			}
 
 
 			activeSkin = skeleton.Skin;
 			activeSkin = skeleton.Skin;

+ 14 - 5
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SpinePreferences.cs

@@ -89,7 +89,10 @@ namespace Spine.Unity.Editor {
 		internal const float DEFAULT_SCENE_ICONS_SCALE = 1f;
 		internal const float DEFAULT_SCENE_ICONS_SCALE = 1f;
 		public const string SCENE_ICONS_SCALE_KEY = "SPINE_SCENE_ICONS_SCALE";
 		public const string SCENE_ICONS_SCALE_KEY = "SPINE_SCENE_ICONS_SCALE";
 
 
-	#if NEW_PREFERENCES_SETTINGS_PROVIDER
+		public const bool DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME = true;
+		public bool mecanimEventIncludeFolderName = DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME;
+
+#if NEW_PREFERENCES_SETTINGS_PROVIDER
 		public static void Load () {
 		public static void Load () {
 			SpineHandles.handleScale = EditorPrefs.GetFloat(SCENE_ICONS_SCALE_KEY, DEFAULT_SCENE_ICONS_SCALE);
 			SpineHandles.handleScale = EditorPrefs.GetFloat(SCENE_ICONS_SCALE_KEY, DEFAULT_SCENE_ICONS_SCALE);
 			GetOrCreateSettings();
 			GetOrCreateSettings();
@@ -121,11 +124,11 @@ namespace Spine.Unity.Editor {
 				EditorGUI.BeginChangeCheck();
 				EditorGUI.BeginChangeCheck();
 				EditorGUILayout.PropertyField(settings.FindProperty("showHierarchyIcons"), new GUIContent("Show Hierarchy Icons", "Show relevant icons on GameObjects with Spine Components on them. Disable this if you have large, complex scenes."));
 				EditorGUILayout.PropertyField(settings.FindProperty("showHierarchyIcons"), new GUIContent("Show Hierarchy Icons", "Show relevant icons on GameObjects with Spine Components on them. Disable this if you have large, complex scenes."));
 				if (EditorGUI.EndChangeCheck()) {
 				if (EditorGUI.EndChangeCheck()) {
-					#if NEWPLAYMODECALLBACKS
+#if NEWPLAYMODECALLBACKS
 					SpineEditorUtilities.HierarchyHandler.IconsOnPlaymodeStateChanged(PlayModeStateChange.EnteredEditMode);
 					SpineEditorUtilities.HierarchyHandler.IconsOnPlaymodeStateChanged(PlayModeStateChange.EnteredEditMode);
-					#else
+#else
 					SpineEditorUtilities.HierarchyHandler.IconsOnPlaymodeStateChanged();
 					SpineEditorUtilities.HierarchyHandler.IconsOnPlaymodeStateChanged();
-					#endif
+#endif
 				}
 				}
 
 
 				EditorGUILayout.PropertyField(settings.FindProperty("autoReloadSceneSkeletons"), new GUIContent("Auto-reload scene components", "Reloads Skeleton components in the scene whenever their SkeletonDataAsset is modified. This makes it so changes in the SkeletonDataAsset inspector are immediately reflected. This may be slow when your scenes have large numbers of SkeletonRenderers or SkeletonGraphic."));
 				EditorGUILayout.PropertyField(settings.FindProperty("autoReloadSceneSkeletons"), new GUIContent("Auto-reload scene components", "Reloads Skeleton components in the scene whenever their SkeletonDataAsset is modified. This makes it so changes in the SkeletonDataAsset inspector are immediately reflected. This may be slow when your scenes have large numbers of SkeletonRenderers or SkeletonGraphic."));
@@ -155,6 +158,12 @@ namespace Spine.Unity.Editor {
 					EditorGUILayout.PropertyField(settings.FindProperty("defaultInstantiateLoop"), new GUIContent("Default Loop", "Spawn Spine GameObjects with loop enabled."));
 					EditorGUILayout.PropertyField(settings.FindProperty("defaultInstantiateLoop"), new GUIContent("Default Loop", "Spawn Spine GameObjects with loop enabled."));
 				}
 				}
 
 
+				EditorGUILayout.Space();
+				EditorGUILayout.LabelField("Mecanim Bake Settings", EditorStyles.boldLabel);
+				{
+					EditorGUILayout.PropertyField(settings.FindProperty("mecanimEventIncludeFolderName"), new GUIContent("Include Folder Name in Event", "When enabled, Mecanim events will call methods named 'FolderNameEventName', when disabled it will call 'EventName'."));
+				}
+				
 				EditorGUILayout.Space();
 				EditorGUILayout.Space();
 				EditorGUILayout.LabelField("Handles and Gizmos", EditorStyles.boldLabel);
 				EditorGUILayout.LabelField("Handles and Gizmos", EditorStyles.boldLabel);
 				{
 				{
@@ -179,6 +188,6 @@ namespace Spine.Unity.Editor {
 			}
 			}
 			EditorGUIUtility.labelWidth = prevLabelWidth;
 			EditorGUIUtility.labelWidth = prevLabelWidth;
 		}
 		}
-	#endif // NEW_PREFERENCES_SETTINGS_PROVIDER
+#endif // NEW_PREFERENCES_SETTINGS_PROVIDER
 	}
 	}
 }
 }

+ 3 - 3
spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs

@@ -333,9 +333,9 @@ namespace Spine.Unity.AttachmentTools {
 			var originalRegions = new List<AtlasRegion>();
 			var originalRegions = new List<AtlasRegion>();
 			int newRegionIndex = 0;
 			int newRegionIndex = 0;
 			
 			
-			foreach (DictionaryEntry skinEntry in skinAttachments) {
-				var originalKey = (Skin.SkinEntry)skinEntry.Key;
-				var originalAttachment = (Attachment)skinEntry.Value;
+			foreach (var skinEntry in skinAttachments) {
+				var originalKey = skinEntry.Key;
+				var originalAttachment = skinEntry.Value;
 
 
 				Attachment newAttachment;
 				Attachment newAttachment;
 				if (IsRenderable(originalAttachment)) {
 				if (IsRenderable(originalAttachment)) {

+ 9 - 9
spine-unity/Assets/Spine/Runtime/spine-unity/Utility/SkinUtilities.cs

@@ -74,7 +74,7 @@ namespace Spine.Unity.AttachmentTools {
 			var newSkin = new Skin(original.name + " clone");
 			var newSkin = new Skin(original.name + " clone");
 			var newSkinAttachments = newSkin.Attachments;
 			var newSkinAttachments = newSkin.Attachments;
 
 
-			foreach (DictionaryEntry a in original.Attachments)
+			foreach (var a in original.Attachments)
 				newSkinAttachments[a.Key] = a.Value;
 				newSkinAttachments[a.Key] = a.Value;
 
 
 			return newSkin;
 			return newSkin;
@@ -129,21 +129,21 @@ namespace Spine.Unity.AttachmentTools {
 
 
 			if (cloneAttachments) {
 			if (cloneAttachments) {
 				if (overwrite) {
 				if (overwrite) {
-					foreach (DictionaryEntry e in sourceAttachments)
-						destinationAttachments[e.Key] = ((Attachment)e.Value).GetCopy(cloneMeshesAsLinked);
+					foreach (var e in sourceAttachments)
+						destinationAttachments[e.Key] = e.Value.GetCopy(cloneMeshesAsLinked);
 				} else {
 				} else {
-					foreach (DictionaryEntry e in sourceAttachments) {
-						if (destinationAttachments.Contains(e.Key)) continue;
-						destinationAttachments.Add(e.Key, ((Attachment)e.Value).GetCopy(cloneMeshesAsLinked));
+					foreach (var e in sourceAttachments) {
+						if (destinationAttachments.ContainsKey(e.Key)) continue;
+						destinationAttachments.Add(e.Key, e.Value.GetCopy(cloneMeshesAsLinked));
 					}
 					}
 				}
 				}
 			} else {
 			} else {
 				if (overwrite) {
 				if (overwrite) {
-					foreach (DictionaryEntry e in sourceAttachments)
+					foreach (var e in sourceAttachments)
 						destinationAttachments[e.Key] = e.Value;
 						destinationAttachments[e.Key] = e.Value;
 				} else {
 				} else {
-					foreach (DictionaryEntry e in sourceAttachments) {
-						if (destinationAttachments.Contains(e.Key)) continue;
+					foreach (var e in sourceAttachments) {
+						if (destinationAttachments.ContainsKey(e.Key)) continue;
 						destinationAttachments.Add(e.Key, e.Value);
 						destinationAttachments.Add(e.Key, e.Value);
 					}
 					}
 				}
 				}