diff --git a/editor source/Desktop/ALV/AccordionColumn.cs b/editor source/Desktop/ALV/AccordionColumn.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e7c898e0c15fa3de8f2588aff7ae8ed5056207fb
--- /dev/null
+++ b/editor source/Desktop/ALV/AccordionColumn.cs	
@@ -0,0 +1,38 @@
+using System.Windows.Forms;
+
+namespace Desktop.CommonControls
+{
+	public class AccordionColumn
+	{
+		/// <summary>
+		/// Column fill weight
+		/// </summary>
+		public float FillWeight { get; set; } = 0;
+
+		/// <summary>
+		/// Fixed column width
+		/// </summary>
+		public int Width { get; set; } = 100;
+
+		/// <summary>
+		/// Property to bind to this column
+		/// </summary>
+		public string PropertyName { get; set; }
+
+		/// <summary>
+		/// Header text
+		/// </summary>
+		public string Text { get; set; } = "";
+
+		/// <summary>
+		/// Column text alignment
+		/// </summary>
+		public HorizontalAlignment TextAlign { get; set; } = HorizontalAlignment.Left;
+
+		public AccordionColumn(string text, string propertyName)
+		{
+			PropertyName = propertyName;
+			Text = text;
+		}
+	}
+}
diff --git a/editor source/Desktop/ALV/FormatRowEventArgs.cs b/editor source/Desktop/ALV/FormatRowEventArgs.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a1e0f503e048f0117d83b31ed155cfa90372dd85
--- /dev/null
+++ b/editor source/Desktop/ALV/FormatRowEventArgs.cs	
@@ -0,0 +1,54 @@
+using System;
+using System.Drawing;
+
+namespace Desktop.CommonControls
+{
+	public class FormatRowEventArgs : EventArgs
+	{
+		public object Model { get; private set; }
+		public Color ForeColor { get; set; }
+		public string Tooltip { get; set; }
+		public Color GrouperColor { get; set; }
+		public GroupedListGrouper Group { get; private set; }
+
+		public FormatRowEventArgs(object model, GroupedListGrouper grouper)
+		{
+			Model = model;
+			Group = grouper;
+		}
+	}
+
+	public class FormatGroupEventArgs : EventArgs
+	{
+		public GroupedListGrouper Group { get; private set; }
+		public string Label { get; set; }
+		public Color ForeColor { get; set; }
+		public Font Font { get; set; }
+
+		public FormatGroupEventArgs(GroupedListGrouper group, Font font)
+		{
+			Group = group;
+			Label = group.Key;
+			ForeColor = Color.RoyalBlue;
+			Font = font;
+		}
+	}
+
+	public class AccordionListViewEventArgs : EventArgs
+	{
+		public GroupedListGrouper Group { get; private set; }
+		public object Model { get; private set; }
+
+		public AccordionListViewEventArgs(object item)
+		{
+			if (item is GroupedListGrouper)
+			{
+				Group = item as GroupedListGrouper;
+			}
+			else
+			{
+				Model = item;
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/CommonControls/AccordionListView.Designer.cs b/editor source/Desktop/CommonControls/AccordionListView.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..376890b0f9c712c789e33f3ffede28a68c361d20
--- /dev/null
+++ b/editor source/Desktop/CommonControls/AccordionListView.Designer.cs	
@@ -0,0 +1,80 @@
+namespace Desktop.CommonControls
+{
+	partial class AccordionListView
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.components = new System.ComponentModel.Container();
+			this.view = new System.Windows.Forms.ListView();
+			this.tmrTick = new System.Windows.Forms.Timer(this.components);
+			this.SuspendLayout();
+			// 
+			// view
+			// 
+			this.view.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.view.FullRowSelect = true;
+			this.view.HideSelection = false;
+			this.view.Location = new System.Drawing.Point(0, 0);
+			this.view.MultiSelect = false;
+			this.view.Name = "view";
+			this.view.OwnerDraw = true;
+			this.view.ShowItemToolTips = true;
+			this.view.Size = new System.Drawing.Size(150, 150);
+			this.view.TabIndex = 0;
+			this.view.UseCompatibleStateImageBehavior = false;
+			this.view.View = System.Windows.Forms.View.Details;
+			this.view.VirtualMode = true;
+			this.view.DrawColumnHeader += new System.Windows.Forms.DrawListViewColumnHeaderEventHandler(this.View_DrawColumnHeader);
+			this.view.DrawItem += new System.Windows.Forms.DrawListViewItemEventHandler(this.View_DrawItem);
+			this.view.DrawSubItem += new System.Windows.Forms.DrawListViewSubItemEventHandler(this.View_DrawSubItem);
+			this.view.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.View_RetrieveVirtualItem);
+			this.view.SelectedIndexChanged += new System.EventHandler(this.View_SelectedIndexChanged);
+			this.view.SizeChanged += new System.EventHandler(this.View_SizeChanged);
+			this.view.MouseClick += new System.Windows.Forms.MouseEventHandler(this.View_MouseClick);
+			this.view.MouseDown += new System.Windows.Forms.MouseEventHandler(this.View_MouseDown);
+			// 
+			// tmrTick
+			// 
+			this.tmrTick.Interval = 1;
+			this.tmrTick.Tick += new System.EventHandler(this.tmrTick_Tick);
+			// 
+			// AccordionListView
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.view);
+			this.Name = "AccordionListView";
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private System.Windows.Forms.ListView view;
+		private System.Windows.Forms.Timer tmrTick;
+	}
+}
diff --git a/editor source/Desktop/CommonControls/AccordionListView.cs b/editor source/Desktop/CommonControls/AccordionListView.cs
new file mode 100644
index 0000000000000000000000000000000000000000..17312d86e8ae2a75cd3205695cf23856646f51de
--- /dev/null
+++ b/editor source/Desktop/CommonControls/AccordionListView.cs	
@@ -0,0 +1,525 @@
+using Desktop.Skinning;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Reflection;
+using System.Windows.Forms;
+
+namespace Desktop.CommonControls
+{
+	public partial class AccordionListView : UserControl, ISkinControl
+	{
+		private StringFormat _sf = new StringFormat() { LineAlignment = StringAlignment.Center, Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.NoWrap };
+		private bool _resizing;
+		private bool _suspendFormatting;
+
+		private List<AccordionColumn> _columns = new List<AccordionColumn>();
+		private HashSet<ListViewItem> _refreshedItems = new HashSet<ListViewItem>();
+		private IGroupedList _source;
+
+		public event EventHandler<EventArgs> SelectedIndexChanged;
+		public event EventHandler<FormatRowEventArgs> FormatRow;
+		public event EventHandler<FormatGroupEventArgs> FormatGroup;
+		public event EventHandler<AccordionListViewEventArgs> RightClick;
+
+		public bool ShowIndicators { get; set; }
+
+		private object _movingObject;
+		private bool _directSelection;
+
+		public AccordionListView()
+		{
+			InitializeComponent();
+
+			Type lv = view.GetType();
+			PropertyInfo pi = lv.GetProperty("DoubleBuffered", BindingFlags.NonPublic | BindingFlags.Instance);
+			pi.SetValue(view, true);
+			view.ColumnWidthChanged += View_ColumnWidthChanged;
+			view.KeyDown += View_KeyDown;
+		}
+
+		private void View_KeyDown(object sender, KeyEventArgs e)
+		{
+			OnKeyDown(e);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			view.BackColor = skin.FieldBackColor;
+		}
+
+		public void ClearColumns()
+		{
+			_columns.Clear();
+		}
+		public void AddColumn(AccordionColumn column)
+		{
+			_columns.Add(column);
+		}
+
+		public void RebuildColumns()
+		{
+			view.Columns.Clear();
+			foreach (AccordionColumn c in _columns)
+			{
+				ColumnHeader col = new ColumnHeader();
+				col.Text = c.Text;
+				col.Tag = c;
+				col.Width = c.Width;
+				col.TextAlign = c.TextAlign;
+				view.Columns.Add(col);
+			}
+			ResizeColumns();
+		}
+
+		public IGroupedList DataSource
+		{
+			get { return _source; }
+			set
+			{
+				if (_source != value)
+				{
+					//scroll back to the top or visual oddities happen with the virtual list
+					if (view.Items.Count > 0)
+					{
+						_suspendFormatting = true;
+						view.TopItem = view.Items[0];
+						_suspendFormatting = false;
+					}
+
+					if (_source != null)
+					{
+						_source.BeforeMovingItem -= _source_BeforeMovingItem;
+						_source.AfterMovingItem -= _source_AfterMovingItem;
+						_source.ListChanged -= _source_ListChanged;
+					}
+					_source = value;
+					if (_source == null)
+					{
+						view.VirtualListSize = 0;
+					}
+					else
+					{
+						view.VirtualListSize = _source.Count;
+						_source.BeforeMovingItem += _source_BeforeMovingItem;
+						_source.AfterMovingItem += _source_AfterMovingItem;
+						_source.ListChanged += _source_ListChanged;
+					}
+				}
+			}
+		}
+
+		private void _source_BeforeMovingItem(object sender, GroupedListMovingEventArgs e)
+		{
+			object selected = SelectedItem;
+			if (selected == e.Item)
+			{
+				_movingObject = selected;
+			}
+		}
+
+		private void _source_AfterMovingItem(object sender, GroupedListMovingEventArgs e)
+		{
+			if (_movingObject != null)
+			{
+				SelectedItem = _movingObject;
+				_movingObject = null;
+			}
+		}
+
+		private void _source_ListChanged(object sender, GroupedListChangedEventArgs e)
+		{
+			switch (e.Action)
+			{
+				case GroupedListChangedAction.Add:
+					if (e.Index == -1) { return; }
+					int currentItemIndex = _source.GetIndex(SelectedItem);
+					view.VirtualListSize++;
+					if (currentItemIndex == e.Index)
+					{
+						//if adding into the same index as the selected item, quietly reselect the current item
+						view.SelectedIndices.Clear();
+						view.SelectedIndices.Add(e.Index + 1);
+					}
+					break;
+				case GroupedListChangedAction.Remove:
+					if (e.Index == -1) { return; }
+					view.VirtualListSize--;
+					View_SelectedIndexChanged(this, EventArgs.Empty);
+					break;
+				case GroupedListChangedAction.Modify:
+					if (e.Index == -1) { return; }
+					AccordionListViewItem listItem = view.Items[e.Index] as AccordionListViewItem;
+					view.BeginUpdate();
+					UpdateRow(listItem, e.Item);
+					view.EndUpdate();
+					break;
+				case GroupedListChangedAction.Clear:
+					view.VirtualListSize = 0;
+					break;
+				case GroupedListChangedAction.GroupToggled:
+					view.VirtualListSize = _source.Count;
+					break;
+			}
+		}
+
+		private void View_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
+		{
+			GroupedListItem groupItem = _source.GetItem(e.ItemIndex);
+			AccordionListViewItem item = new AccordionListViewItem(groupItem);
+			item.Tag = groupItem.Data;
+			UpdateRow(item, groupItem.Data);
+			e.Item = item;
+		} 
+
+		private void UpdateRow(AccordionListViewItem item, object model)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			Type type = model.GetType();
+
+			GroupedListGrouper grouper = model as GroupedListGrouper;
+			if (grouper != null)
+			{
+				//Grouper
+				FormatGroupEventArgs args = new FormatGroupEventArgs(grouper, new Font(view.Font, FontStyle.Bold));
+				args.ForeColor = skin.PrimaryForeColor;
+				if (!_suspendFormatting)
+				{
+					FormatGroup?.Invoke(this, args);
+				}
+				item.Text = args.Label;
+				while (item.SubItems.Count < _columns.Count)
+				{
+					item.SubItems.Add("");
+				}
+				item.ForeColor = Enabled ? args.ForeColor : skin.PrimaryColor.DisabledForeColor;
+				item.Font = args.Font;
+			}
+			else
+			{
+				//Item
+				FormatRowEventArgs args = new FormatRowEventArgs(model, item.Item.Group);
+				args.GrouperColor = item.GrouperColor;
+				args.ForeColor = skin.Surface.ForeColor;
+				if (!_suspendFormatting)
+				{
+					FormatRow?.Invoke(this, args);
+				}
+
+				item.ForeColor = Enabled ? args.ForeColor : skin.Surface.DisabledForeColor;
+				item.GrouperColor = args.GrouperColor;
+				item.Font = view.Font;
+				item.ToolTipText = args.Tooltip;
+
+				for (int i = 0; i < _columns.Count; i++)
+				{
+					string propName = _columns[i].PropertyName;
+					string value = "";
+					if (string.IsNullOrEmpty(propName))
+					{
+						value = "Unbound";
+					}
+					else
+					{
+						MemberInfo mi = PropertyTypeInfo.GetMemberInfo(type, propName);
+						if (mi != null)
+						{
+							value = mi.GetValue(model)?.ToString() ?? "";
+						}
+					}
+					if (i == 0)
+					{
+						item.Text = value;
+					}
+					else
+					{
+						if (item.SubItems.Count > i)
+						{
+							item.SubItems[i].Text = value;
+						}
+						else
+						{
+							item.SubItems.Add(value);
+						}
+					}
+				}
+			}
+		}
+
+		private void View_SizeChanged(object sender, EventArgs e)
+		{
+			ResizeColumns();
+		}
+
+		private void ResizeColumns()
+		{
+			if (_resizing) { return; }
+			_resizing = true;
+
+			float totalWeight = 0;
+			float usedWidth = 0;
+			foreach (ColumnHeader col in view.Columns)
+			{
+				AccordionColumn c = col.Tag as AccordionColumn;
+				totalWeight += c.FillWeight;
+				if (c.FillWeight == 0)
+				{
+					usedWidth += c.Width;
+				}
+			}
+
+			for (int i = 0; i < view.Columns.Count; i++)
+			{
+				AccordionColumn c = view.Columns[i].Tag as AccordionColumn;
+				if (c.FillWeight > 0)
+				{
+					float percent = c.FillWeight / totalWeight;
+					view.Columns[i].Width = Math.Max(20, (int)(percent * (view.ClientRectangle.Width - usedWidth)));
+				}
+			}
+
+			_resizing = false;
+		}
+
+		private void View_SelectedIndexChanged(object sender, EventArgs e)
+		{
+			if (_movingObject == null)
+			{
+				if (_directSelection)
+				{
+					tmrTick.Enabled = false;
+					SelectedIndexChanged?.Invoke(this, e);
+				}
+				else
+				{
+					tmrTick.Enabled = true;
+				}
+			}
+		}
+
+		private void tmrTick_Tick(object sender, EventArgs e)
+		{
+			tmrTick.Enabled = false;
+			SelectedIndexChanged?.Invoke(this, e);
+		}
+
+		public object SelectedItem
+		{
+			get
+			{
+				return view.SelectedIndices.Count > 0 ? view.Items[view.SelectedIndices[0]].Tag : null;
+			}
+			set
+			{
+				if (_source == null || value == SelectedItem) { return; }
+				_directSelection = true;
+				if (value != null)
+				{
+					_source.ExpandTo(value);
+					int index = _source.GetIndex(value);
+					if (index >= 0)
+					{
+						view.Items[index].Selected = true;
+						view.EnsureVisible(index);
+					}
+				}
+				else
+				{
+					if (view.SelectedIndices.Count > 0)
+					{
+						view.Items[view.SelectedIndices[0]].Selected = false;
+					}
+				}
+				_directSelection = false;
+			}
+		}
+
+		public void ExpandAll()
+		{
+			_source?.ExpandAll();
+		}
+
+		private void View_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			Graphics g = e.Graphics;
+			SolidBrush back = skin.Surface.GetBrush(VisualState.Normal, false, true);
+			g.FillRectangle(back, e.Bounds);
+			using (SolidBrush fore = new SolidBrush(skin.Surface.ForeColor))
+			{
+				Rectangle textRect = new Rectangle(e.Bounds.X + 5, e.Bounds.Y, e.Bounds.Width - 5, e.Bounds.Height);
+				g.DrawString(e.Header.Text, Font, fore, textRect, _sf);
+			}
+			Pen border = skin.Surface.GetBorderPen(VisualState.Normal, false, true);
+			g.DrawLine(border, e.Bounds.X, e.Bounds.Bottom - 1, e.Bounds.Right, e.Bounds.Bottom - 1);
+			if (e.ColumnIndex > 0)
+			{
+				g.DrawLine(border, e.Bounds.X, e.Bounds.Y, e.Bounds.X, e.Bounds.Bottom - 2);
+			}
+		}
+
+		private void View_DrawItem(object sender, DrawListViewItemEventArgs e)
+		{
+			return;
+		}
+
+		private void View_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
+		{
+			Graphics g = e.Graphics;
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
+			if (e.Item.Selected && e.ColumnIndex == 0)
+			{
+				g.FillRectangle(skin.PrimaryColor.GetBrush(VisualState.Focused, false, Enabled), e.Item.Bounds);
+				e.DrawFocusRectangle(e.Item.Bounds);
+			}
+
+			using (StringFormat sf = new StringFormat())
+			{
+				// Store the column text alignment, letting it default
+				// to Left if it has not been set to Center or Right.
+				switch (e.Header.TextAlign)
+				{
+					case HorizontalAlignment.Center:
+						sf.Alignment = StringAlignment.Center;
+						break;
+					case HorizontalAlignment.Right:
+						sf.Alignment = StringAlignment.Far;
+						break;
+				}
+				sf.FormatFlags = StringFormatFlags.NoWrap;
+				sf.Trimming = StringTrimming.EllipsisCharacter;
+
+				AccordionListViewItem item = e.Item as AccordionListViewItem;
+				int depthShift = 0;
+				if (item.Tag is GroupedListGrouper)
+				{
+					int depth = ((GroupedListGrouper)item.Tag).Depth;
+					depthShift += Properties.Resources.Collapse.Width * depth;
+				}
+				Rectangle itemBounds = new Rectangle(e.Bounds.X + 4 + depthShift, e.Bounds.Y + 1, e.Bounds.Width - 4 - depthShift, e.Bounds.Height);
+
+				Rectangle bounds = e.Bounds;
+				bounds = itemBounds;
+				if (e.ColumnIndex == 0)
+				{
+					if (item.Tag is GroupedListGrouper)
+					{
+						if (!item.Selected)
+						{
+							using (SolidBrush back = new SolidBrush(skin.FieldBackColor))
+							{
+								g.FillRectangle(back, new Rectangle(0, itemBounds.Y, view.Width, itemBounds.Height));
+							}
+						}
+						Pen divider = skin.PrimaryColor.GetBorderPen(VisualState.Normal, false, Enabled);
+						GroupedListGrouper group = item.Tag as GroupedListGrouper;
+						Image img = group.Expanded ? Properties.Resources.Collapse : Properties.Resources.Expand;
+						bounds = new Rectangle(itemBounds.X + img.Width, itemBounds.Y - 1, view.ClientRectangle.Width - (itemBounds.X + img.Width), itemBounds.Height);
+						if (_source.GetGroupCount(group.Path) > 0)
+						{
+							g.DrawImage(img, itemBounds.X, itemBounds.Y - 2);
+						}
+						g.DrawLine(divider, 5, e.Bounds.Bottom - 2, view.ClientRectangle.Width - 5, e.Bounds.Bottom - 2);
+
+						if (ShowIndicators)
+						{
+							using (SolidBrush br = new SolidBrush(item.ForeColor))
+							{
+								g.FillRectangle(br, e.Bounds.X, e.Bounds.Y, 3, e.Bounds.Height);
+							}
+						}
+					}
+				}
+				else if (item.Tag is GroupedListGrouper)
+				{
+					return;
+				}
+
+				if (item.Selected)
+				{
+					using (SolidBrush textBrush = new SolidBrush(Enabled ? skin.PrimaryColor.ForeColor : skin.PrimaryColor.DisabledForeColor))
+					{
+						g.DrawString(e.SubItem.Text, item.Font, textBrush, bounds, sf);
+					}
+				}
+				else
+				{
+					if (!(item.Tag is GroupedListGrouper))
+					{
+						int depth = _source.GetDepth(item.Tag);
+						using (SolidBrush back = new SolidBrush(depth % 2 == 0 ? skin.FieldBackColor : skin.FieldAltBackColor))
+						{
+							g.FillRectangle(back, e.Bounds);
+						}
+						if (e.ColumnIndex == 0 && ShowIndicators)
+						{
+							using (SolidBrush br = new SolidBrush(item.GrouperColor))
+							{
+								g.FillRectangle(br, e.Bounds.X, e.Bounds.Y, 3, e.Bounds.Height);
+							}
+						}
+					}
+					using (SolidBrush br = new SolidBrush(item.ForeColor))
+					{
+						g.DrawString(e.SubItem.Text, item.Font, br, bounds, sf);
+					}
+				}
+
+				if (item.Item.LastInGroup)
+				{
+					Pen divider = skin.PrimaryColor.GetBorderPen(VisualState.Normal, false, Enabled);
+					g.DrawLine(divider, 5, e.Bounds.Bottom - 2, view.ClientRectangle.Width - 5, e.Bounds.Bottom - 2);
+				}
+			}
+		}
+
+		private void View_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
+		{
+			view.Invalidate();
+		}
+
+		private void View_MouseDown(object sender, MouseEventArgs e)
+		{
+			if (_source == null) { return; }
+			ListViewItem item = view.GetItemAt(e.X, e.Y);
+			if (item != null && item.Tag is GroupedListGrouper)
+			{
+				GroupedListGrouper grouper = item.Tag as GroupedListGrouper;
+				int depth = grouper.Depth;
+				int targetX = 4 + depth * Properties.Resources.Collapse.Width;
+				if (e.X < targetX || e.X > targetX + Properties.Resources.Collapse.Width)
+				{
+					return;
+				}
+				if (_source.GetGroupCount(grouper.Path) > 0)
+				{
+					_source.ToggleGroup(grouper.Path, !grouper.Expanded);
+				}
+			}
+		}
+
+		private void View_MouseClick(object sender, MouseEventArgs e)
+		{
+			if (e.Button == MouseButtons.Right)
+			{
+				if (view.FocusedItem.Bounds.Contains(e.Location))
+				{
+					RightClick?.Invoke(this, new AccordionListViewEventArgs(view.FocusedItem.Tag));
+				}
+			}
+		}	
+	}
+
+	public class AccordionListViewItem : ListViewItem
+	{
+		public GroupedListItem Item { get; private set; }
+
+		public Color GrouperColor { get; set; } = Color.Black;
+
+		public AccordionListViewItem(GroupedListItem item)
+		{
+			Item = item;
+		}
+	}
+}
diff --git a/editor source/Desktop/CommonControls/AccordionListView.resx b/editor source/Desktop/CommonControls/AccordionListView.resx
new file mode 100644
index 0000000000000000000000000000000000000000..2bc86b0be9c05dc939cf6f21d9471309d21a5246
--- /dev/null
+++ b/editor source/Desktop/CommonControls/AccordionListView.resx	
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="tmrTick.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/Desktop/CommonControls/ColorField.cs b/editor source/Desktop/CommonControls/ColorField.cs
new file mode 100644
index 0000000000000000000000000000000000000000..820d7213b0bb72e9b80a1c67ecf7dcbc86671ba6
--- /dev/null
+++ b/editor source/Desktop/CommonControls/ColorField.cs	
@@ -0,0 +1,38 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.CommonControls
+{
+	public class ColorField : Button
+	{
+		private ColorDialog _dialog;
+		public event EventHandler ColorChanged;
+
+		public ColorField()
+		{
+			FlatStyle = FlatStyle.Flat;
+			_dialog = new ColorDialog();
+		}
+
+		public Color Color
+		{
+			get { return BackColor; }
+			set
+			{
+				BackColor = value;
+				ColorChanged?.Invoke(this, EventArgs.Empty);
+			}
+		}
+
+		protected override void OnClick(EventArgs e)
+		{
+			base.OnClick(e);
+			_dialog.Color = Color;
+			if (_dialog.ShowDialog() == DialogResult.OK)
+			{
+				Color = _dialog.Color;
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/CommonControls/DBPanel.cs b/editor source/Desktop/CommonControls/DBPanel.cs
index 3c23a4a45545addb8921b597eae30d70dd619f75..b7cd611e7933e5fa613db3f3137de976d10e9a97 100644
--- a/editor source/Desktop/CommonControls/DBPanel.cs	
+++ b/editor source/Desktop/CommonControls/DBPanel.cs	
@@ -1,9 +1,10 @@
-using System.Drawing;
+using Desktop.Skinning;
+using System.Drawing;
 using System.Windows.Forms;
 
 namespace Desktop.CommonControls
 {
-	public class DBPanel : Panel
+	public class DBPanel : SkinnedPanel
 	{
 		public DBPanel()
 		{
diff --git a/editor source/Desktop/CommonControls/DBTreeView.cs b/editor source/Desktop/CommonControls/DBTreeView.cs
index ac4c3ac610a59d994b9836de709269f613cfb0fb..e91b6f6da9cbd7da58cbc5238d5912271b3ce7d0 100644
--- a/editor source/Desktop/CommonControls/DBTreeView.cs	
+++ b/editor source/Desktop/CommonControls/DBTreeView.cs	
@@ -1,10 +1,11 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Windows.Forms;
 
 namespace Desktop.CommonControls
 {
 	//Double-buffered TreeView. The DoubleBuffered property doesn't do anything, so we have to take this lower-level route
-	public class DBTreeView : TreeView
+	public class DBTreeView : TreeView, ISkinControl
 	{
 		protected override void OnHandleCreated(EventArgs e)
 		{
@@ -12,6 +13,12 @@ namespace Desktop.CommonControls
 			base.OnHandleCreated(e);
 		}
 
+		public void OnUpdateSkin(Skin skin)
+		{
+			BackColor = skin.FieldBackColor;
+			ForeColor = skin.Surface.ForeColor;
+		}
+
 		// Pinvoke:
 		private const int TVM_SETEXTENDEDSTYLE = 0x1100 + 44;
 		private const int TVM_GETEXTENDEDSTYLE = 0x1100 + 45;
diff --git a/editor source/Desktop/CommonControls/DataGridView/RecordCell.cs b/editor source/Desktop/CommonControls/DataGridView/RecordCell.cs
new file mode 100644
index 0000000000000000000000000000000000000000..404e4fadf309e7385534ee92e2b18fdaebb15a56
--- /dev/null
+++ b/editor source/Desktop/CommonControls/DataGridView/RecordCell.cs	
@@ -0,0 +1,73 @@
+using System;
+using System.Windows.Forms;
+
+namespace Desktop.CommonControls
+{
+	public class RecordCell : DataGridViewTextBoxCell
+	{
+		private RecordEditingControl EditingControl { get; set; }
+
+		private Type _recordType;
+		public Type RecordType
+		{
+			get { return _recordType; }
+			set
+			{
+				if (_recordType != value)
+				{
+					_recordType = value;
+					if (OwnsEditingControl(RowIndex))
+					{
+						EditingControl.RecordType = value;
+					}
+				}
+			}
+		}
+
+		private bool OwnsEditingControl(int rowIndex)
+		{
+			return rowIndex != -1 && EditingControl != null && rowIndex == EditingControl.EditingControlRowIndex;
+		}
+
+		public override Type EditType
+		{
+			get { return typeof(RecordEditingControl); }
+		}
+
+		public override object Clone()
+		{
+			RecordCell copy = base.Clone() as RecordCell;
+			if (copy != null)
+			{
+				copy.RecordType = RecordType;
+			}
+			return copy;
+		}
+
+		public override void DetachEditingControl()
+		{
+			if (EditingControl != null)
+			{
+				EditingControl = null;
+			}
+			base.DetachEditingControl();
+		}
+
+		public override Type FormattedValueType
+		{
+			get { return typeof(string); }
+		}
+
+		public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
+		{
+			base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
+			RecordEditingControl control = DataGridView.EditingControl as RecordEditingControl;
+			if (control != null)
+			{
+				control.RecordType = RecordType;
+				control.RecordKey = initialFormattedValue?.ToString();
+				EditingControl = control;
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/CommonControls/DataGridView/RecordColumn.cs b/editor source/Desktop/CommonControls/DataGridView/RecordColumn.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c8a39a54a9d6a23de1aff98c6d8112f0907b256d
--- /dev/null
+++ b/editor source/Desktop/CommonControls/DataGridView/RecordColumn.cs	
@@ -0,0 +1,45 @@
+using System;
+using System.Windows.Forms;
+
+namespace Desktop.CommonControls
+{
+	public class RecordColumn : DataGridViewColumn
+	{
+		private RecordCell _cellTemplate;
+
+		public RecordColumn()
+		{
+			CellTemplate = _cellTemplate = new RecordCell();
+		}
+
+		public Type RecordType
+		{
+			get { return _cellTemplate.RecordType; }
+			set
+			{
+				_cellTemplate.RecordType = value;
+				if (DataGridView != null)
+				{
+					DataGridViewRowCollection rows = DataGridView.Rows;
+					for (int i = 0; i < rows.Count; i++)
+					{
+						DataGridViewRow row = rows.SharedRow(i);
+						RecordCell cell = row.Cells[Index] as RecordCell;
+						if (cell != null)
+						{
+							cell.RecordType = value;
+						}
+					}
+					DataGridView.InvalidateColumn(Index);
+				}
+			}
+		}
+
+		public override object Clone()
+		{
+			RecordColumn copy = base.Clone() as RecordColumn;
+			copy.RecordType = RecordType;
+			return copy;
+		}
+	}
+}
diff --git a/editor source/Desktop/CommonControls/DataGridView/RecordEditingControl.cs b/editor source/Desktop/CommonControls/DataGridView/RecordEditingControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..733de87605350a89da89ba82ad6418c20573876c
--- /dev/null
+++ b/editor source/Desktop/CommonControls/DataGridView/RecordEditingControl.cs	
@@ -0,0 +1,121 @@
+using System;
+using System.Globalization;
+using System.Windows.Forms;
+
+namespace Desktop.CommonControls
+{
+	public class RecordEditingControl : RecordField, IDataGridViewEditingControl
+	{
+		private DataGridView _dataGridView;
+		private bool _valueChanged;
+		private int _rowIndex;
+
+		public RecordEditingControl() : base()
+		{
+			TabStop = false;
+		}
+
+		public virtual DataGridView EditingControlDataGridView
+		{
+			get
+			{
+				return _dataGridView;
+			}
+			set
+			{
+				_dataGridView = value;
+			}
+		}
+
+		public virtual object EditingControlFormattedValue
+		{
+			get
+			{
+				return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting);
+			}
+			set
+			{
+				string valueStr = value as string;
+				if (valueStr != null)
+				{
+					Text = valueStr;
+				}
+			}
+		}
+
+		public virtual int EditingControlRowIndex
+		{
+			get
+			{
+				return _rowIndex;
+			}
+			set
+			{
+				_rowIndex = value;
+			}
+		}
+
+		public virtual bool EditingControlValueChanged
+		{
+			get
+			{
+				return _valueChanged;
+			}
+			set
+			{
+				_valueChanged = value;
+			}
+		}
+
+		public virtual Cursor EditingPanelCursor
+		{
+			get
+			{
+				return Cursors.Default;
+			}
+		}
+
+		public virtual bool RepositionEditingControlOnValueChange
+		{
+			get
+			{
+				return false;
+			}
+		}
+
+		public virtual bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey)
+		{
+			return !dataGridViewWantsInputKey;
+		}
+
+		public virtual object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
+		{
+			ValidateField();
+			return RecordKey ?? "";
+		}
+
+		public virtual void PrepareEditingControlForEdit(bool selectAll)
+		{
+			if (selectAll)
+			{
+				SelectAll();
+			}
+		}
+
+		protected override void OnRecordChanged()
+		{
+			NotifyDataGridViewOfValueChange();
+		}
+
+		private void NotifyDataGridViewOfValueChange()
+		{
+			_valueChanged = true;
+			_dataGridView.NotifyCurrentCellDirty(true);
+		}
+
+		public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
+		{
+			
+		}
+	}
+}
diff --git a/editor source/Desktop/CommonControls/DataSlicerControl.Designer.cs b/editor source/Desktop/CommonControls/DataSlicerControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e8c9b3dc3af2acf1a2b8792f089d593f8503a096
--- /dev/null
+++ b/editor source/Desktop/CommonControls/DataSlicerControl.Designer.cs	
@@ -0,0 +1,284 @@
+namespace Desktop.CommonControls
+{
+	partial class DataSlicerControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+				_labelBrush.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.components = new System.ComponentModel.Container();
+			this.tmrRefresh = new System.Windows.Forms.Timer(this.components);
+			this.skinnedSplitContainer1 = new Desktop.Skinning.SkinnedSplitContainer();
+			this.skinnedSplitContainer2 = new Desktop.Skinning.SkinnedSplitContainer();
+			this.skinnedGroupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.toolStrip1 = new System.Windows.Forms.ToolStrip();
+			this.tsAddSlice = new System.Windows.Forms.ToolStripButton();
+			this.tsRemoveSlice = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+			this.tsDown = new System.Windows.Forms.ToolStripButton();
+			this.tsUp = new System.Windows.Forms.ToolStripButton();
+			this.lstSlices = new Desktop.Skinning.SkinnedListBox();
+			this.grpProperties = new Desktop.Skinning.SkinnedGroupBox();
+			this.pnlSlice = new System.Windows.Forms.Panel();
+			this.skinnedGroupBox3 = new Desktop.Skinning.SkinnedGroupBox();
+			this.graph = new Desktop.Skinning.SkinnedPanel();
+			((System.ComponentModel.ISupportInitialize)(this.skinnedSplitContainer1)).BeginInit();
+			this.skinnedSplitContainer1.Panel1.SuspendLayout();
+			this.skinnedSplitContainer1.Panel2.SuspendLayout();
+			this.skinnedSplitContainer1.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.skinnedSplitContainer2)).BeginInit();
+			this.skinnedSplitContainer2.Panel1.SuspendLayout();
+			this.skinnedSplitContainer2.Panel2.SuspendLayout();
+			this.skinnedSplitContainer2.SuspendLayout();
+			this.skinnedGroupBox1.SuspendLayout();
+			this.toolStrip1.SuspendLayout();
+			this.grpProperties.SuspendLayout();
+			this.skinnedGroupBox3.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// tmrRefresh
+			// 
+			this.tmrRefresh.Tick += new System.EventHandler(this.tmrRefresh_Tick);
+			// 
+			// skinnedSplitContainer1
+			// 
+			this.skinnedSplitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.skinnedSplitContainer1.Location = new System.Drawing.Point(0, 0);
+			this.skinnedSplitContainer1.Name = "skinnedSplitContainer1";
+			// 
+			// skinnedSplitContainer1.Panel1
+			// 
+			this.skinnedSplitContainer1.Panel1.Controls.Add(this.skinnedSplitContainer2);
+			// 
+			// skinnedSplitContainer1.Panel2
+			// 
+			this.skinnedSplitContainer1.Panel2.Controls.Add(this.skinnedGroupBox3);
+			this.skinnedSplitContainer1.Size = new System.Drawing.Size(1004, 668);
+			this.skinnedSplitContainer1.SplitterColor = Desktop.Skinning.SkinnedBackgroundType.Primary;
+			this.skinnedSplitContainer1.SplitterDistance = 220;
+			this.skinnedSplitContainer1.TabIndex = 0;
+			// 
+			// skinnedSplitContainer2
+			// 
+			this.skinnedSplitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.skinnedSplitContainer2.Location = new System.Drawing.Point(0, 0);
+			this.skinnedSplitContainer2.Name = "skinnedSplitContainer2";
+			this.skinnedSplitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal;
+			// 
+			// skinnedSplitContainer2.Panel1
+			// 
+			this.skinnedSplitContainer2.Panel1.Controls.Add(this.skinnedGroupBox1);
+			// 
+			// skinnedSplitContainer2.Panel2
+			// 
+			this.skinnedSplitContainer2.Panel2.Controls.Add(this.grpProperties);
+			this.skinnedSplitContainer2.Size = new System.Drawing.Size(220, 668);
+			this.skinnedSplitContainer2.SplitterColor = Desktop.Skinning.SkinnedBackgroundType.Primary;
+			this.skinnedSplitContainer2.SplitterDistance = 249;
+			this.skinnedSplitContainer2.TabIndex = 0;
+			// 
+			// skinnedGroupBox1
+			// 
+			this.skinnedGroupBox1.Controls.Add(this.toolStrip1);
+			this.skinnedGroupBox1.Controls.Add(this.lstSlices);
+			this.skinnedGroupBox1.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.skinnedGroupBox1.Location = new System.Drawing.Point(0, 0);
+			this.skinnedGroupBox1.Name = "skinnedGroupBox1";
+			this.skinnedGroupBox1.Size = new System.Drawing.Size(220, 249);
+			this.skinnedGroupBox1.TabIndex = 1;
+			this.skinnedGroupBox1.TabStop = false;
+			this.skinnedGroupBox1.Text = "Slices";
+			// 
+			// toolStrip1
+			// 
+			this.toolStrip1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.toolStrip1.AutoSize = false;
+			this.toolStrip1.Dock = System.Windows.Forms.DockStyle.None;
+			this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
+			this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.tsAddSlice,
+            this.tsRemoveSlice,
+            this.toolStripSeparator1,
+            this.tsDown,
+            this.tsUp});
+			this.toolStrip1.Location = new System.Drawing.Point(6, 25);
+			this.toolStrip1.Name = "toolStrip1";
+			this.toolStrip1.Size = new System.Drawing.Size(208, 25);
+			this.toolStrip1.TabIndex = 1;
+			this.toolStrip1.Tag = "Surface";
+			this.toolStrip1.Text = "toolStrip1";
+			// 
+			// tsAddSlice
+			// 
+			this.tsAddSlice.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsAddSlice.Image = global::Desktop.Properties.Resources.Add;
+			this.tsAddSlice.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsAddSlice.Name = "tsAddSlice";
+			this.tsAddSlice.Size = new System.Drawing.Size(23, 22);
+			this.tsAddSlice.Text = "Add Slice";
+			this.tsAddSlice.Click += new System.EventHandler(this.tsAddSlice_Click);
+			// 
+			// tsRemoveSlice
+			// 
+			this.tsRemoveSlice.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsRemoveSlice.Image = global::Desktop.Properties.Resources.Remove;
+			this.tsRemoveSlice.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsRemoveSlice.Name = "tsRemoveSlice";
+			this.tsRemoveSlice.Size = new System.Drawing.Size(23, 22);
+			this.tsRemoveSlice.Text = "Remove Slice";
+			this.tsRemoveSlice.Click += new System.EventHandler(this.tsRemoveSlice_Click);
+			// 
+			// toolStripSeparator1
+			// 
+			this.toolStripSeparator1.Name = "toolStripSeparator1";
+			this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
+			// 
+			// tsDown
+			// 
+			this.tsDown.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsDown.Image = global::Desktop.Properties.Resources.DownArrow;
+			this.tsDown.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsDown.Name = "tsDown";
+			this.tsDown.Size = new System.Drawing.Size(23, 22);
+			this.tsDown.Text = "Move Down";
+			this.tsDown.Click += new System.EventHandler(this.tsDown_Click);
+			// 
+			// tsUp
+			// 
+			this.tsUp.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsUp.Image = global::Desktop.Properties.Resources.UpArrow;
+			this.tsUp.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsUp.Name = "tsUp";
+			this.tsUp.Size = new System.Drawing.Size(23, 22);
+			this.tsUp.Text = "Move Up";
+			this.tsUp.Click += new System.EventHandler(this.tsUp_Click);
+			// 
+			// lstSlices
+			// 
+			this.lstSlices.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstSlices.BackColor = System.Drawing.Color.White;
+			this.lstSlices.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstSlices.ForeColor = System.Drawing.Color.Black;
+			this.lstSlices.FormattingEnabled = true;
+			this.lstSlices.IntegralHeight = false;
+			this.lstSlices.Location = new System.Drawing.Point(6, 53);
+			this.lstSlices.Name = "lstSlices";
+			this.lstSlices.Size = new System.Drawing.Size(208, 190);
+			this.lstSlices.TabIndex = 0;
+			this.lstSlices.SelectedIndexChanged += new System.EventHandler(this.lstSlices_SelectedIndexChanged);
+			// 
+			// grpProperties
+			// 
+			this.grpProperties.Controls.Add(this.pnlSlice);
+			this.grpProperties.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.grpProperties.Location = new System.Drawing.Point(0, 0);
+			this.grpProperties.Name = "grpProperties";
+			this.grpProperties.Size = new System.Drawing.Size(220, 415);
+			this.grpProperties.TabIndex = 0;
+			this.grpProperties.TabStop = false;
+			this.grpProperties.Text = "Slice Properties";
+			// 
+			// pnlSlice
+			// 
+			this.pnlSlice.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.pnlSlice.Location = new System.Drawing.Point(6, 27);
+			this.pnlSlice.Name = "pnlSlice";
+			this.pnlSlice.Size = new System.Drawing.Size(208, 380);
+			this.pnlSlice.TabIndex = 0;
+			// 
+			// skinnedGroupBox3
+			// 
+			this.skinnedGroupBox3.Controls.Add(this.graph);
+			this.skinnedGroupBox3.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.skinnedGroupBox3.Location = new System.Drawing.Point(0, 0);
+			this.skinnedGroupBox3.Name = "skinnedGroupBox3";
+			this.skinnedGroupBox3.Size = new System.Drawing.Size(780, 668);
+			this.skinnedGroupBox3.TabIndex = 1;
+			this.skinnedGroupBox3.TabStop = false;
+			this.skinnedGroupBox3.Text = "Data";
+			// 
+			// graph
+			// 
+			this.graph.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.graph.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+			this.graph.Location = new System.Drawing.Point(6, 22);
+			this.graph.Name = "graph";
+			this.graph.PanelType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.graph.Size = new System.Drawing.Size(768, 640);
+			this.graph.TabIndex = 0;
+			this.graph.TabSide = Desktop.Skinning.TabSide.None;
+			this.graph.Paint += new System.Windows.Forms.PaintEventHandler(this.graph_Paint);
+			// 
+			// DataSlicerControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.skinnedSplitContainer1);
+			this.Name = "DataSlicerControl";
+			this.Size = new System.Drawing.Size(1004, 668);
+			this.skinnedSplitContainer1.Panel1.ResumeLayout(false);
+			this.skinnedSplitContainer1.Panel2.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.skinnedSplitContainer1)).EndInit();
+			this.skinnedSplitContainer1.ResumeLayout(false);
+			this.skinnedSplitContainer2.Panel1.ResumeLayout(false);
+			this.skinnedSplitContainer2.Panel2.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.skinnedSplitContainer2)).EndInit();
+			this.skinnedSplitContainer2.ResumeLayout(false);
+			this.skinnedGroupBox1.ResumeLayout(false);
+			this.toolStrip1.ResumeLayout(false);
+			this.toolStrip1.PerformLayout();
+			this.grpProperties.ResumeLayout(false);
+			this.skinnedGroupBox3.ResumeLayout(false);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private Skinning.SkinnedSplitContainer skinnedSplitContainer1;
+		private Skinning.SkinnedSplitContainer skinnedSplitContainer2;
+		private Skinning.SkinnedGroupBox skinnedGroupBox1;
+		private Skinning.SkinnedListBox lstSlices;
+		private Skinning.SkinnedGroupBox grpProperties;
+		private System.Windows.Forms.Panel pnlSlice;
+		private System.Windows.Forms.ToolStrip toolStrip1;
+		private System.Windows.Forms.ToolStripButton tsAddSlice;
+		private Skinning.SkinnedGroupBox skinnedGroupBox3;
+		private Desktop.Skinning.SkinnedPanel graph;
+		private System.Windows.Forms.Timer tmrRefresh;
+		private System.Windows.Forms.ToolStripButton tsRemoveSlice;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
+		private System.Windows.Forms.ToolStripButton tsDown;
+		private System.Windows.Forms.ToolStripButton tsUp;
+	}
+}
diff --git a/editor source/Desktop/CommonControls/DataSlicerControl.cs b/editor source/Desktop/CommonControls/DataSlicerControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5fb836ee99e9716a2885fc982e08de5556944ed5
--- /dev/null
+++ b/editor source/Desktop/CommonControls/DataSlicerControl.cs	
@@ -0,0 +1,227 @@
+using Desktop.Providers;
+using Desktop.Reporting;
+using Desktop.Skinning;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.CommonControls
+{
+	public partial class DataSlicerControl : UserControl, ISkinControl
+	{
+		private DataSlicer _slicer;
+		private IEnumerable<ISliceable> _dataSet;
+		private Control _currentEditControl;
+
+		private const int BucketPadding = 10;
+		private const int MaxBarWidth = 200;
+
+		private SolidBrush[] _barBrushes;
+		private SolidBrush _labelBrush = new SolidBrush(Color.Black);
+
+		private List<DataBucket> _buckets = null;
+
+		public DataSlicerControl()
+		{
+			_barBrushes = new SolidBrush[8];
+			for (int i = 0; i < _barBrushes.Length; i++)
+			{
+				_barBrushes[i] = new SolidBrush(Color.Black);
+			}
+			InitializeComponent();
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			_barBrushes[0].Color = skin.Blue;
+			_barBrushes[1].Color = skin.Red;
+			_barBrushes[2].Color = skin.Green;
+			_barBrushes[3].Color = skin.Orange;
+			_barBrushes[4].Color = skin.Purple;
+			_barBrushes[5].Color = skin.Gray;
+			_barBrushes[6].Color = skin.Pink;
+			_barBrushes[7].Color = skin.LightGray;
+			_labelBrush.Color = skin.Surface.ForeColor;
+		}
+
+		public void SetSlicer(DataSlicer slicer, IEnumerable<ISliceable> dataSet)
+		{
+			_slicer = slicer;
+			_slicer.PropertyChanged += _slicer_PropertyChanged;
+			_slicer.Slicers.CollectionChanged += Slicers_CollectionChanged;
+			_dataSet = dataSet;
+			SliceData();
+		}
+
+		private void Slicers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+		{
+			switch (e.Action)
+			{
+				case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
+					IDataSlicer slicer = e.NewItems[0] as IDataSlicer;
+					lstSlices.Items.Insert(e.NewStartingIndex, slicer);
+					lstSlices.SelectedItem = slicer;
+					SliceData();
+					break;
+				case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
+					lstSlices.Items.RemoveAt(e.OldStartingIndex);
+					break;
+			}
+		}
+
+		private void SliceData()
+		{
+			tmrRefresh.Start();
+		}
+
+		private void graph_Paint(object sender, PaintEventArgs e)
+		{
+			if (_buckets == null || _buckets.Count == 0)
+			{
+				return;
+			}
+
+			Graphics g = e.Graphics;
+			g.Clear(SkinManager.Instance.CurrentSkin.FieldBackColor);
+			int width = (graph.Width - BucketPadding * 2) / _buckets.Count;
+			int height = graph.Height;
+			int x = BucketPadding;
+			int y = 0;
+			int maxCount = 0;
+			foreach (DataBucket b in _buckets)
+			{
+				if (b.Count > maxCount)
+				{
+					maxCount = b.Count;
+				}
+			}
+
+			for (int i = 0; i < _buckets.Count; i++)
+			{
+				DrawBucket(g, _buckets[i], x, y, width, height, maxCount, _buckets);
+				x += width;
+			}
+		}
+
+		private void DrawBucket(Graphics g, DataBucket bucket, int x, int y, int width, int height, int maxCount, List<DataBucket> siblings)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
+			//label
+			StringFormat sf = new StringFormat() { LineAlignment = StringAlignment.Far, Alignment = StringAlignment.Center, FormatFlags = StringFormatFlags.NoWrap, Trimming = StringTrimming.EllipsisCharacter };
+			Rectangle rect = new Rectangle(x, y, width, height);
+			g.DrawString(bucket.Name, Skin.TextFont, _labelBrush, rect, sf);
+
+			int lineHeight = (int)g.MeasureString("A", Skin.TextFont).Height;
+			//bars
+			if (bucket.SubBuckets.Count > 0)
+			{
+				//recursive labels
+				height -= lineHeight;
+				width = (width - BucketPadding * 2) / bucket.SubBuckets.Count;
+				x += BucketPadding;
+				for (int i = 0; i < bucket.SubBuckets.Count; i++)
+				{
+					DataBucket sub = bucket.SubBuckets[i];
+					//label
+					DrawBucket(g, sub, x, y, width, height, maxCount, bucket.SubBuckets);
+
+					x += width;
+				}
+			}
+			else
+			{
+				int i = siblings.IndexOf(bucket);
+				SolidBrush barBrush = _barBrushes[i % _barBrushes.Length];
+				int availableHeight = height - lineHeight * 2;
+				float pct = maxCount == 0 ? 0 : bucket.Count / (float)maxCount;
+				int barHeight = Math.Max(1, (int)(pct * availableHeight));
+				int barWidth = Math.Min(MaxBarWidth, width);
+				int left = x + width / 2 - barWidth / 2;
+				g.FillRectangle(barBrush, left, availableHeight - barHeight + lineHeight, barWidth, barHeight);
+				g.DrawString(bucket.Count.ToString(), Skin.TextFont, barBrush, new Rectangle(left, availableHeight - barHeight, barWidth, lineHeight), sf);
+			}
+		}
+
+		private void tsAddSlice_Click(object sender, EventArgs e)
+		{
+			SlicerDefinition def = RecordLookup.DoLookup(typeof(SlicerDefinition), "", false, null) as SlicerDefinition;
+			if (def != null)
+			{
+				IDataSlicer slicer = def.CreateInstance(_slicer.Context);
+				_slicer.AddSlicer(slicer);
+			}
+		}
+
+		private void tsRemoveSlice_Click(object sender, EventArgs e)
+		{
+			IDataSlicer slicer = lstSlices.SelectedItem as IDataSlicer;
+			if (slicer != null)
+			{
+				_slicer.RemoveSlicer(slicer);
+			}
+		}
+
+		private void tsDown_Click(object sender, EventArgs e)
+		{
+			IDataSlicer slicer = lstSlices.SelectedItem as IDataSlicer;
+			if (slicer != null)
+			{
+				_slicer.MoveSlicer(slicer, 1);
+			}
+		}
+
+		private void tsUp_Click(object sender, EventArgs e)
+		{
+			IDataSlicer slicer = lstSlices.SelectedItem as IDataSlicer;
+			if (slicer != null)
+			{
+				_slicer.MoveSlicer(slicer, -1);
+			}
+		}
+
+		private void _slicer_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			SliceData();
+		}
+
+		private void lstSlices_SelectedIndexChanged(object sender, EventArgs e)
+		{
+			if (_currentEditControl != null)
+			{
+				pnlSlice.Controls.Remove(_currentEditControl);
+				_currentEditControl = null;
+			}
+			IDataSlicer slicer = lstSlices.SelectedItem as IDataSlicer;
+			if (slicer != null)
+			{
+				grpProperties.Text = $"{slicer.DisplayName} Properties";
+				Type controlType = DataSlicerControlMap.GetControlType(slicer.GetType());
+				if (controlType != null)
+				{
+					Control ctl = Activator.CreateInstance(controlType) as Control;
+					ISlicerControl slicerCtl = ctl as ISlicerControl;
+					if (ctl != null && slicerCtl != null)
+					{
+						_currentEditControl = ctl;
+						ctl.Dock = DockStyle.Fill;
+						pnlSlice.Controls.Add(ctl);
+						slicerCtl.SetSlicer(slicer);
+					}
+				}
+			}
+			else
+			{
+				grpProperties.Text = "Slice Properties";
+			}
+		}
+
+		private void tmrRefresh_Tick(object sender, EventArgs e)
+		{
+			tmrRefresh.Stop();
+			_buckets = _slicer.Slice(_dataSet);
+			graph.Invalidate();
+		}
+	}
+}
diff --git a/editor source/Desktop/CommonControls/DataSlicerControl.resx b/editor source/Desktop/CommonControls/DataSlicerControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..69a801f225be4350ec0606b540726c5fc4436c77
--- /dev/null
+++ b/editor source/Desktop/CommonControls/DataSlicerControl.resx	
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="tmrRefresh.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>122, 17</value>
+  </metadata>
+  <metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/Desktop/CommonControls/GroupRadioButton.cs b/editor source/Desktop/CommonControls/GroupRadioButton.cs
index 390ef9a5786a6bd6eb896a9a8e664b30b99ab837..29dec682310539000c02eab5f5f3c50fdb013bbe 100644
--- a/editor source/Desktop/CommonControls/GroupRadioButton.cs	
+++ b/editor source/Desktop/CommonControls/GroupRadioButton.cs	
@@ -1,9 +1,10 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Windows.Forms;
 
 namespace Desktop.CommonControls
 {
-	public class GroupRadioButton : RadioButton
+	public class GroupRadioButton : SkinnedRadioButton
 	{
 		public string GroupName { get; set; }
 
diff --git a/editor source/Desktop/CommonControls/ListSelect.cs b/editor source/Desktop/CommonControls/ListSelect.cs
deleted file mode 100644
index 4d3f5168d03aabca4c250aa0d86c5caf83243b71..0000000000000000000000000000000000000000
--- a/editor source/Desktop/CommonControls/ListSelect.cs	
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.Collections.Generic;
-using System.Windows.Forms;
-
-namespace Desktop.CommonControls
-{
-	public partial class ListSelect : Form
-	{
-		public ListSelect()
-		{
-			InitializeComponent();
-		}
-
-		public void SetItems(string label, List<object> items)
-		{
-			lblName.Text = label;
-			cboItems.Items.Clear();
-			cboItems.Items.AddRange(items.ToArray());
-		}
-
-		public object SelectedItem
-		{
-			get
-			{
-				return cboItems.SelectedItem;
-			}
-		}
-
-		private void cmdOK_Click(object sender, System.EventArgs e)
-		{
-			DialogResult = DialogResult.OK;
-			Close();
-		}
-
-		private void cmdCancel_Click(object sender, System.EventArgs e)
-		{
-			DialogResult = DialogResult.Cancel;
-			Close();
-		}
-	}
-}
diff --git a/editor source/Desktop/CommonControls/ListSelect.designer.cs b/editor source/Desktop/CommonControls/ListSelect.designer.cs
deleted file mode 100644
index a9823290ed562063a81422b0aa00cf160bee352d..0000000000000000000000000000000000000000
--- a/editor source/Desktop/CommonControls/ListSelect.designer.cs	
+++ /dev/null
@@ -1,105 +0,0 @@
-namespace Desktop.CommonControls
-{
-	partial class ListSelect
-	{
-		/// <summary>
-		/// Required designer variable.
-		/// </summary>
-		private System.ComponentModel.IContainer components = null;
-
-		/// <summary>
-		/// Clean up any resources being used.
-		/// </summary>
-		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
-		protected override void Dispose(bool disposing)
-		{
-			if (disposing && (components != null))
-			{
-				components.Dispose();
-			}
-			base.Dispose(disposing);
-		}
-
-		#region Windows Form Designer generated code
-
-		/// <summary>
-		/// Required method for Designer support - do not modify
-		/// the contents of this method with the code editor.
-		/// </summary>
-		private void InitializeComponent()
-		{
-			this.lblName = new System.Windows.Forms.Label();
-			this.cboItems = new System.Windows.Forms.ComboBox();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.SuspendLayout();
-			// 
-			// lblName
-			// 
-			this.lblName.AutoSize = true;
-			this.lblName.Location = new System.Drawing.Point(12, 15);
-			this.lblName.Name = "lblName";
-			this.lblName.Size = new System.Drawing.Size(40, 13);
-			this.lblName.TabIndex = 0;
-			this.lblName.Text = "Select:";
-			// 
-			// cboItems
-			// 
-			this.cboItems.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
-			this.cboItems.FormattingEnabled = true;
-			this.cboItems.Location = new System.Drawing.Point(95, 12);
-			this.cboItems.Name = "cboItems";
-			this.cboItems.Size = new System.Drawing.Size(177, 21);
-			this.cboItems.TabIndex = 1;
-			// 
-			// cmdOK
-			// 
-			this.cmdOK.Location = new System.Drawing.Point(116, 39);
-			this.cmdOK.Name = "cmdOK";
-			this.cmdOK.Size = new System.Drawing.Size(75, 23);
-			this.cmdOK.TabIndex = 2;
-			this.cmdOK.Text = "OK";
-			this.cmdOK.UseVisualStyleBackColor = true;
-			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
-			// 
-			// cmdCancel
-			// 
-			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(197, 39);
-			this.cmdCancel.Name = "cmdCancel";
-			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
-			this.cmdCancel.TabIndex = 3;
-			this.cmdCancel.Text = "Cancel";
-			this.cmdCancel.UseVisualStyleBackColor = true;
-			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
-			// 
-			// ListSelect
-			// 
-			this.AcceptButton = this.cmdOK;
-			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(284, 73);
-			this.ControlBox = false;
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
-			this.Controls.Add(this.cboItems);
-			this.Controls.Add(this.lblName);
-			this.Name = "ListSelect";
-			this.ShowIcon = false;
-			this.ShowInTaskbar = false;
-			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
-			this.Text = "Select an Item";
-			this.ResumeLayout(false);
-			this.PerformLayout();
-
-		}
-
-		#endregion
-
-		private System.Windows.Forms.Label lblName;
-		private System.Windows.Forms.ComboBox cboItems;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
-	}
-}
\ No newline at end of file
diff --git a/editor source/Desktop/CommonControls/NumericField.Designer.cs b/editor source/Desktop/CommonControls/NumericField.Designer.cs
index 9172a67431f5381f52611255fb5a9a157263958b..bb6053f21e4c0c93049a0033ef3f79c875ab5f26 100644
--- a/editor source/Desktop/CommonControls/NumericField.Designer.cs	
+++ b/editor source/Desktop/CommonControls/NumericField.Designer.cs	
@@ -28,7 +28,7 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.valField = new System.Windows.Forms.NumericUpDown();
+			this.valField = new Desktop.Skinning.SkinnedNumericUpDown();
 			this.lblPlaceholder = new System.Windows.Forms.Label();
 			((System.ComponentModel.ISupportInitialize)(this.valField)).BeginInit();
 			this.SuspendLayout();
@@ -73,7 +73,7 @@
 
 		#endregion
 
-		private System.Windows.Forms.NumericUpDown valField;
+		private Desktop.Skinning.SkinnedNumericUpDown valField;
 		private System.Windows.Forms.Label lblPlaceholder;
 	}
 }
diff --git a/editor source/Desktop/CommonControls/NumericField.cs b/editor source/Desktop/CommonControls/NumericField.cs
index 572cff49cc62693e2962687e9c6dfd7ededc3405..9c5c221f3524e1fbcf6a5148017b235735d39ee1 100644
--- a/editor source/Desktop/CommonControls/NumericField.cs	
+++ b/editor source/Desktop/CommonControls/NumericField.cs	
@@ -1,11 +1,12 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.ComponentModel;
 using System.Globalization;
 using System.Windows.Forms;
 
 namespace Desktop.CommonControls
 {
-	public partial class NumericField : UserControl, ISupportInitialize
+	public partial class NumericField : UserControl, ISupportInitialize, ISkinControl
 	{
 		public decimal Minimum
 		{
@@ -112,5 +113,11 @@ namespace Desktop.CommonControls
 		{
 			lblPlaceholder.Visible = IsPlaceholderVisible;
 		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			lblPlaceholder.BackColor = skin.FieldBackColor;
+			lblPlaceholder.ForeColor = skin.Surface.DisabledForeColor;
+		}
 	}
 }
diff --git a/editor source/Desktop/CommonControls/PropertyControls/BooleanControl.Designer.cs b/editor source/Desktop/CommonControls/PropertyControls/BooleanControl.Designer.cs
index 57f5c1ad59e47ce3ceafe0eaf56740ce60de4711..26bc473c4fe8260c69489c3eba5e47eb87f66435 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/BooleanControl.Designer.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/BooleanControl.Designer.cs	
@@ -28,7 +28,7 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.chkEnabled = new System.Windows.Forms.CheckBox();
+			this.chkEnabled = new Desktop.Skinning.SkinnedCheckBox();
 			this.SuspendLayout();
 			// 
 			// chkEnabled
@@ -56,6 +56,6 @@
 
 		#endregion
 
-		private System.Windows.Forms.CheckBox chkEnabled;
+		private Desktop.Skinning.SkinnedCheckBox chkEnabled;
 	}
 }
diff --git a/editor source/Desktop/CommonControls/PropertyControls/BooleanControl.cs b/editor source/Desktop/CommonControls/PropertyControls/BooleanControl.cs
index 3063359342cca602111262fc11101752f3491ad4..2058322966af4b4ca9edb40ebf7fdb947893664d 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/BooleanControl.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/BooleanControl.cs	
@@ -72,12 +72,12 @@ namespace Desktop.CommonControls.PropertyControls
 			Save();
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			chkEnabled.Checked = false;
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			bool enabled = chkEnabled.Checked;
 			if (PropertyType == typeof(bool))
diff --git a/editor source/Desktop/CommonControls/PropertyControls/ColorControl.Designer.cs b/editor source/Desktop/CommonControls/PropertyControls/ColorControl.Designer.cs
index c5411d254a2a13a5f1cef84918b98cda8d422070..047633107cf9c50ad7df36b7eaace25dc51bf8cc 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/ColorControl.Designer.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/ColorControl.Designer.cs	
@@ -29,7 +29,7 @@
 		private void InitializeComponent()
 		{
 			this.colorPicker = new System.Windows.Forms.ColorDialog();
-			this.label1 = new System.Windows.Forms.Label();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
 			this.cmdColor = new System.Windows.Forms.Button();
 			this.txtValue = new Desktop.CommonControls.TextField();
 			this.SuspendLayout();
@@ -42,6 +42,10 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
 			this.label1.Location = new System.Drawing.Point(87, 3);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(14, 13);
@@ -61,8 +65,13 @@
 			// 
 			// txtValue
 			// 
+			this.txtValue.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.txtValue.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
 			this.txtValue.Location = new System.Drawing.Point(102, 0);
+			this.txtValue.Multiline = false;
 			this.txtValue.Name = "txtValue";
+			this.txtValue.PlaceholderText = "";
+			this.txtValue.ReadOnly = false;
 			this.txtValue.Size = new System.Drawing.Size(49, 20);
 			this.txtValue.TabIndex = 3;
 			// 
@@ -84,7 +93,7 @@
 
 		private System.Windows.Forms.Button cmdColor;
 		private System.Windows.Forms.ColorDialog colorPicker;
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedLabel label1;
 		private TextField txtValue;
 	}
 }
diff --git a/editor source/Desktop/CommonControls/PropertyControls/ColorControl.cs b/editor source/Desktop/CommonControls/PropertyControls/ColorControl.cs
index 8dc1be398e80d5e9dabff0a44e824728c34a9ee4..42ab79e3853daa5cf1e137b4da2ca27ff041f8cd 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/ColorControl.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/ColorControl.cs	
@@ -15,26 +15,41 @@ namespace Desktop.CommonControls.PropertyControls
 
 		protected override void OnBoundData()
 		{
-			string value = GetValue()?.ToString();
-			if (string.IsNullOrEmpty(value))
+			if (DataType == typeof(Color))
 			{
-				cmdColor.BackColor = Color.Empty;
-				_cleared = true;
+				Color value = (Color)GetValue();
+				if (value == Color.Empty)
+				{
+					_cleared = true;
+				}
+				else
+				{
+					cmdColor.BackColor = value;
+				}
 			}
 			else
 			{
-				try
-				{
-					Color color = ColorTranslator.FromHtml(value);
-					txtValue.Text = value.Substring(1);
-					cmdColor.BackColor = color;
-					_cleared = false;
-				}
-				catch
+				string value = GetValue()?.ToString();
+				if (string.IsNullOrEmpty(value))
 				{
 					cmdColor.BackColor = Color.Empty;
 					_cleared = true;
 				}
+				else
+				{
+					try
+					{
+						Color color = ColorTranslator.FromHtml(value);
+						txtValue.Text = value.Substring(1);
+						cmdColor.BackColor = color;
+						_cleared = false;
+					}
+					catch
+					{
+						cmdColor.BackColor = Color.Empty;
+						_cleared = true;
+					}
+				}
 			}
 		}
 
@@ -82,7 +97,7 @@ namespace Desktop.CommonControls.PropertyControls
 			catch { }
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			RemoveHandlers();
 			cmdColor.BackColor = Color.Transparent;
@@ -92,16 +107,30 @@ namespace Desktop.CommonControls.PropertyControls
 			Save();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
-			if (_cleared)
+			if (DataType == typeof(Color))
 			{
-				SetValue(null);
+				if (_cleared)
+				{
+					SetValue(Color.Empty);
+				}
+				else
+				{
+					SetValue(cmdColor.BackColor);
+				}
 			}
 			else
 			{
-				string value = "#" + ToHexValue(cmdColor.BackColor);
-				SetValue(value);
+				if (_cleared)
+				{
+					SetValue(null);
+				}
+				else
+				{
+					string value = "#" + ToHexValue(cmdColor.BackColor);
+					SetValue(value);
+				}
 			}
 		}
 
diff --git a/editor source/Desktop/CommonControls/PropertyControls/ColorControl.resx b/editor source/Desktop/CommonControls/PropertyControls/ColorControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..548ab62489fbc9504a6e443e9a3ca1f3846bc791
--- /dev/null
+++ b/editor source/Desktop/CommonControls/PropertyControls/ColorControl.resx	
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="colorPicker.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/Desktop/CommonControls/PropertyControls/ColorSetControl.Designer.cs b/editor source/Desktop/CommonControls/PropertyControls/ColorSetControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ca51c70c76a0b7ac37120bbe741e275624e96ea8
--- /dev/null
+++ b/editor source/Desktop/CommonControls/PropertyControls/ColorSetControl.Designer.cs	
@@ -0,0 +1,228 @@
+namespace Desktop.CommonControls.PropertyControls
+{
+	partial class ColorSetControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.components = new System.ComponentModel.Container();
+			this.clrNormal = new Desktop.CommonControls.ColorField();
+			this.clrHover = new Desktop.CommonControls.ColorField();
+			this.clrPressed = new Desktop.CommonControls.ColorField();
+			this.clrSelected = new Desktop.CommonControls.ColorField();
+			this.clrDisabled = new Desktop.CommonControls.ColorField();
+			this.clrDisSelected = new Desktop.CommonControls.ColorField();
+			this.clrText = new Desktop.CommonControls.ColorField();
+			this.clrDisabledText = new Desktop.CommonControls.ColorField();
+			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
+			this.clrBorderHover = new Desktop.CommonControls.ColorField();
+			this.clrBorder = new Desktop.CommonControls.ColorField();
+			this.clrBorderDisabled = new Desktop.CommonControls.ColorField();
+			this.clrBorderSelected = new Desktop.CommonControls.ColorField();
+			this.SuspendLayout();
+			// 
+			// clrNormal
+			// 
+			this.clrNormal.BackColor = System.Drawing.SystemColors.Control;
+			this.clrNormal.Color = System.Drawing.SystemColors.Control;
+			this.clrNormal.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrNormal.Location = new System.Drawing.Point(0, 0);
+			this.clrNormal.Name = "clrNormal";
+			this.clrNormal.Size = new System.Drawing.Size(21, 21);
+			this.clrNormal.TabIndex = 1;
+			this.toolTip1.SetToolTip(this.clrNormal, "Normal");
+			this.clrNormal.UseVisualStyleBackColor = true;
+			// 
+			// clrHover
+			// 
+			this.clrHover.BackColor = System.Drawing.SystemColors.Control;
+			this.clrHover.Color = System.Drawing.SystemColors.Control;
+			this.clrHover.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrHover.Location = new System.Drawing.Point(27, 0);
+			this.clrHover.Name = "clrHover";
+			this.clrHover.Size = new System.Drawing.Size(21, 21);
+			this.clrHover.TabIndex = 3;
+			this.toolTip1.SetToolTip(this.clrHover, "Hover");
+			this.clrHover.UseVisualStyleBackColor = true;
+			// 
+			// clrPressed
+			// 
+			this.clrPressed.BackColor = System.Drawing.SystemColors.Control;
+			this.clrPressed.Color = System.Drawing.SystemColors.Control;
+			this.clrPressed.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrPressed.Location = new System.Drawing.Point(54, 0);
+			this.clrPressed.Name = "clrPressed";
+			this.clrPressed.Size = new System.Drawing.Size(21, 21);
+			this.clrPressed.TabIndex = 5;
+			this.toolTip1.SetToolTip(this.clrPressed, "Pressed");
+			this.clrPressed.UseVisualStyleBackColor = true;
+			// 
+			// clrSelected
+			// 
+			this.clrSelected.BackColor = System.Drawing.SystemColors.Control;
+			this.clrSelected.Color = System.Drawing.SystemColors.Control;
+			this.clrSelected.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrSelected.Location = new System.Drawing.Point(80, 0);
+			this.clrSelected.Name = "clrSelected";
+			this.clrSelected.Size = new System.Drawing.Size(21, 21);
+			this.clrSelected.TabIndex = 7;
+			this.toolTip1.SetToolTip(this.clrSelected, "Selected");
+			this.clrSelected.UseVisualStyleBackColor = true;
+			// 
+			// clrDisabled
+			// 
+			this.clrDisabled.BackColor = System.Drawing.SystemColors.Control;
+			this.clrDisabled.Color = System.Drawing.SystemColors.Control;
+			this.clrDisabled.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrDisabled.Location = new System.Drawing.Point(107, 0);
+			this.clrDisabled.Name = "clrDisabled";
+			this.clrDisabled.Size = new System.Drawing.Size(21, 21);
+			this.clrDisabled.TabIndex = 9;
+			this.toolTip1.SetToolTip(this.clrDisabled, "Disabled");
+			this.clrDisabled.UseVisualStyleBackColor = true;
+			// 
+			// clrDisSelected
+			// 
+			this.clrDisSelected.BackColor = System.Drawing.SystemColors.Control;
+			this.clrDisSelected.Color = System.Drawing.SystemColors.Control;
+			this.clrDisSelected.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrDisSelected.Location = new System.Drawing.Point(134, 0);
+			this.clrDisSelected.Name = "clrDisSelected";
+			this.clrDisSelected.Size = new System.Drawing.Size(21, 21);
+			this.clrDisSelected.TabIndex = 11;
+			this.toolTip1.SetToolTip(this.clrDisSelected, "Selected + Disabled");
+			this.clrDisSelected.UseVisualStyleBackColor = true;
+			// 
+			// clrText
+			// 
+			this.clrText.BackColor = System.Drawing.SystemColors.Control;
+			this.clrText.Color = System.Drawing.SystemColors.Control;
+			this.clrText.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrText.Location = new System.Drawing.Point(161, 0);
+			this.clrText.Name = "clrText";
+			this.clrText.Size = new System.Drawing.Size(21, 21);
+			this.clrText.TabIndex = 12;
+			this.toolTip1.SetToolTip(this.clrText, "Text");
+			this.clrText.UseVisualStyleBackColor = true;
+			// 
+			// clrDisabledText
+			// 
+			this.clrDisabledText.BackColor = System.Drawing.SystemColors.Control;
+			this.clrDisabledText.Color = System.Drawing.SystemColors.Control;
+			this.clrDisabledText.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrDisabledText.Location = new System.Drawing.Point(188, 0);
+			this.clrDisabledText.Name = "clrDisabledText";
+			this.clrDisabledText.Size = new System.Drawing.Size(21, 21);
+			this.clrDisabledText.TabIndex = 14;
+			this.toolTip1.SetToolTip(this.clrDisabledText, "Disabled Text");
+			this.clrDisabledText.UseVisualStyleBackColor = true;
+			// 
+			// clrBorderHover
+			// 
+			this.clrBorderHover.BackColor = System.Drawing.SystemColors.Control;
+			this.clrBorderHover.Color = System.Drawing.SystemColors.Control;
+			this.clrBorderHover.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrBorderHover.Location = new System.Drawing.Point(242, 0);
+			this.clrBorderHover.Name = "clrBorderHover";
+			this.clrBorderHover.Size = new System.Drawing.Size(21, 21);
+			this.clrBorderHover.TabIndex = 16;
+			this.toolTip1.SetToolTip(this.clrBorderHover, "Hover Border");
+			this.clrBorderHover.UseVisualStyleBackColor = true;
+			// 
+			// clrBorder
+			// 
+			this.clrBorder.BackColor = System.Drawing.SystemColors.Control;
+			this.clrBorder.Color = System.Drawing.SystemColors.Control;
+			this.clrBorder.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrBorder.Location = new System.Drawing.Point(215, 0);
+			this.clrBorder.Name = "clrBorder";
+			this.clrBorder.Size = new System.Drawing.Size(21, 21);
+			this.clrBorder.TabIndex = 15;
+			this.toolTip1.SetToolTip(this.clrBorder, "Border");
+			this.clrBorder.UseVisualStyleBackColor = true;
+			// 
+			// clrBorderDisabled
+			// 
+			this.clrBorderDisabled.BackColor = System.Drawing.SystemColors.Control;
+			this.clrBorderDisabled.Color = System.Drawing.SystemColors.Control;
+			this.clrBorderDisabled.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrBorderDisabled.Location = new System.Drawing.Point(296, 0);
+			this.clrBorderDisabled.Name = "clrBorderDisabled";
+			this.clrBorderDisabled.Size = new System.Drawing.Size(21, 21);
+			this.clrBorderDisabled.TabIndex = 17;
+			this.toolTip1.SetToolTip(this.clrBorderDisabled, "Disabled Border");
+			this.clrBorderDisabled.UseVisualStyleBackColor = true;
+			// 
+			// clrBorderSelected
+			// 
+			this.clrBorderSelected.BackColor = System.Drawing.SystemColors.Control;
+			this.clrBorderSelected.Color = System.Drawing.SystemColors.Control;
+			this.clrBorderSelected.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.clrBorderSelected.Location = new System.Drawing.Point(269, 0);
+			this.clrBorderSelected.Name = "clrBorderSelected";
+			this.clrBorderSelected.Size = new System.Drawing.Size(21, 21);
+			this.clrBorderSelected.TabIndex = 18;
+			this.toolTip1.SetToolTip(this.clrBorderSelected, "Selected Border");
+			this.clrBorderSelected.UseVisualStyleBackColor = true;
+			// 
+			// ColorSetControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.clrBorderSelected);
+			this.Controls.Add(this.clrBorderDisabled);
+			this.Controls.Add(this.clrBorderHover);
+			this.Controls.Add(this.clrBorder);
+			this.Controls.Add(this.clrDisabledText);
+			this.Controls.Add(this.clrText);
+			this.Controls.Add(this.clrDisSelected);
+			this.Controls.Add(this.clrDisabled);
+			this.Controls.Add(this.clrSelected);
+			this.Controls.Add(this.clrPressed);
+			this.Controls.Add(this.clrHover);
+			this.Controls.Add(this.clrNormal);
+			this.Name = "ColorSetControl";
+			this.Size = new System.Drawing.Size(326, 21);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+		private ColorField clrNormal;
+		private ColorField clrHover;
+		private ColorField clrPressed;
+		private ColorField clrSelected;
+		private ColorField clrDisabled;
+		private ColorField clrDisSelected;
+		private ColorField clrText;
+		private ColorField clrDisabledText;
+		private System.Windows.Forms.ToolTip toolTip1;
+		private ColorField clrBorderHover;
+		private ColorField clrBorder;
+		private ColorField clrBorderDisabled;
+		private ColorField clrBorderSelected;
+	}
+}
diff --git a/editor source/Desktop/CommonControls/PropertyControls/ColorSetControl.cs b/editor source/Desktop/CommonControls/PropertyControls/ColorSetControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7124b2e08a92de8f997bc6ded0182fc1298cf97f
--- /dev/null
+++ b/editor source/Desktop/CommonControls/PropertyControls/ColorSetControl.cs	
@@ -0,0 +1,173 @@
+using Desktop.Skinning;
+using System;
+using System.Drawing;
+
+namespace Desktop.CommonControls.PropertyControls
+{
+	public partial class ColorSetControl : PropertyEditControl
+	{
+		public ColorSetControl()
+		{
+			InitializeComponent();
+		}
+
+		protected override void OnBoundData()
+		{
+			ColorSet set = GetValue() as ColorSet;
+			clrNormal.Color = set.Normal;
+			clrHover.Color = set.Hover;
+			clrPressed.Color = set.Pressed;
+			clrSelected.Color = set.Selected;
+			clrDisabled.Color = set.Disabled;
+			clrDisSelected.Color = set.DisabledSelected;
+			clrText.Color = set.ForeColor;
+			clrDisabledText.Color = set.DisabledForeColor;
+			clrBorder.Color = set.Border;
+			clrBorderHover.Color = set.BorderHover;
+			clrBorderDisabled.Color = set.BorderDisabled;
+			clrBorderSelected.Color = set.BorderSelected;
+		}
+
+		protected override void AddHandlers()
+		{
+			clrNormal.ColorChanged += Normal_ColorChanged;
+			clrHover.ColorChanged += ColorChanged;
+			clrPressed.ColorChanged += ColorChanged;
+			clrSelected.ColorChanged += ColorChanged;
+			clrDisabled.ColorChanged += ColorChanged;
+			clrDisSelected.ColorChanged += ColorChanged;
+			clrText.ColorChanged += ColorChanged;
+			clrDisabledText.ColorChanged += ColorChanged;
+			clrBorder.ColorChanged += ColorChanged;
+			clrBorderHover.ColorChanged += ColorChanged;
+			clrBorderDisabled.ColorChanged += ColorChanged;
+			clrBorderSelected.ColorChanged += ColorChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			clrNormal.ColorChanged -= Normal_ColorChanged;
+			clrHover.ColorChanged -= ColorChanged;
+			clrPressed.ColorChanged -= ColorChanged;
+			clrSelected.ColorChanged -= ColorChanged;
+			clrDisabled.ColorChanged -= ColorChanged;
+			clrDisSelected.ColorChanged -= ColorChanged;
+			clrText.ColorChanged -= ColorChanged;
+			clrDisabledText.ColorChanged -= ColorChanged;
+			clrBorder.ColorChanged -= ColorChanged;
+			clrBorderHover.ColorChanged -= ColorChanged;
+			clrBorderDisabled.ColorChanged -= ColorChanged;
+			clrBorderSelected.ColorChanged -= ColorChanged;
+		}
+
+		private Color Darken(Color inColor, float factor)
+		{
+			float r = inColor.R / 255.0f;
+			float g = inColor.G / 255.0f;
+			float b = inColor.B / 255.0f;
+
+			r *= 1 - factor;
+			g *= 1 - factor;
+			b *= 1 - factor;
+
+			Color outColor = Color.FromArgb(inColor.A, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
+			return outColor;
+		}
+
+		private Color Lighten(Color inColor, float factor)
+		{
+			float r = inColor.R / 255.0f;
+			float g = inColor.G / 255.0f;
+			float b = inColor.B / 255.0f;
+
+			r = r + (1 - r) * factor;
+			g = g + (1 - g) * factor;
+			b = b + (1 - b) * factor;
+
+			Color outColor = Color.FromArgb(inColor.A, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
+			return outColor;
+		}
+
+		private Color Grayscale(Color inColor)
+		{
+			float r = inColor.R / 255.0f;
+			float g = inColor.G / 255.0f;
+			float b = inColor.B / 255.0f;
+
+			r = g = b = 0.2126f * r + 0.7152f * g + 0.0722f * b;
+
+			Color outColor = Color.FromArgb(inColor.A, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
+			return outColor;
+		}
+
+		private Color GetForeColor(Color backColor)
+		{
+			float r = backColor.R / 255.0f;
+			float g = backColor.G / 255.0f;
+			float b = backColor.B / 255.0f;
+
+			float intensity = 0.299f * r + 0.587f * g + 0.114f * b;
+			if (intensity * 255f > 186)
+			{
+				return Color.Black;
+			}
+			return Color.White;
+		}
+
+		private void Normal_ColorChanged(object sender, EventArgs e)
+		{
+			RemoveHandlers();
+
+			//auto-update all the other colors based on this one
+			Color normal = clrNormal.Color;
+
+			clrHover.Color = Lighten(normal, 0.25f);
+			clrPressed.Color = Darken(normal, 0.25f);
+			clrSelected.Color = Darken(normal, 0.05f);
+			clrDisabled.Color = Grayscale(normal);
+			clrDisSelected.Color = Grayscale(clrSelected.Color);
+			clrText.Color = GetForeColor(normal);
+			clrDisabledText.Color = clrText.Color == Color.White ? Color.FromArgb(180, 180, 180) : Color.FromArgb(70, 70, 70);
+
+			clrBorder.Color = clrPressed.Color;
+			clrBorderHover.Color = normal;
+			clrBorderSelected.Color = Darken(clrBorder.Color, 0.05f);
+			clrBorderDisabled.Color = Grayscale(clrBorder.Color);
+
+			Save();
+			AddHandlers();
+		}
+
+		private void ColorChanged(object sender, EventArgs e)
+		{
+			Save();
+		}
+
+		protected override void OnSave()
+		{
+			ColorSet set = GetValue() as ColorSet;
+			set.Normal = clrNormal.Color;
+			set.Hover = clrHover.Color;
+			set.Pressed = clrPressed.Color;
+			set.Selected = clrSelected.Color;
+			set.Disabled = clrDisabled.Color;
+			set.DisabledSelected = clrDisSelected.Color;
+			set.ForeColor = clrText.Color;
+			set.DisabledForeColor = clrDisabledText.Color;
+			set.Border = clrBorder.Color;
+			set.BorderDisabled = clrBorderDisabled.Color;
+			set.BorderHover = clrBorderHover.Color;
+			set.BorderSelected = clrBorderSelected.Color;
+			NotifyPropertyChanged();
+			set.ClearCache();
+		}
+	}
+
+	public class ColorSetAttribute : EditControlAttribute
+	{
+		public override Type EditControlType
+		{
+			get { return typeof(ColorSetControl); }
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/TagControl.resx b/editor source/Desktop/CommonControls/PropertyControls/ColorSetControl.resx
similarity index 100%
rename from editor source/SPNATI Character Editor/Controls/TagControl.resx
rename to editor source/Desktop/CommonControls/PropertyControls/ColorSetControl.resx
diff --git a/editor source/Desktop/CommonControls/PropertyControls/ComboBoxControl.Designer.cs b/editor source/Desktop/CommonControls/PropertyControls/ComboBoxControl.Designer.cs
index 5f8a37984464714df8b26f0e4f67ea246ee4f73e..9462cb8d782a18f5092bf31ce0be3f35a9347996 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/ComboBoxControl.Designer.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/ComboBoxControl.Designer.cs	
@@ -28,17 +28,25 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.cboItems = new System.Windows.Forms.ComboBox();
+			this.cboItems = new Desktop.Skinning.SkinnedComboBox();
 			this.SuspendLayout();
 			// 
 			// cboItems
 			// 
+			this.cboItems.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboItems.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboItems.BackColor = System.Drawing.Color.White;
 			this.cboItems.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.cboItems.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboItems.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboItems.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboItems.FormattingEnabled = true;
 			this.cboItems.Location = new System.Drawing.Point(0, 0);
 			this.cboItems.Name = "cboItems";
-			this.cboItems.Size = new System.Drawing.Size(175, 21);
+			this.cboItems.SelectedIndex = -1;
+			this.cboItems.SelectedItem = null;
+			this.cboItems.Size = new System.Drawing.Size(175, 25);
+			this.cboItems.Sorted = false;
 			this.cboItems.TabIndex = 0;
 			// 
 			// ComboBoxControl
@@ -54,6 +62,6 @@
 
 		#endregion
 
-		private System.Windows.Forms.ComboBox cboItems;
+		private Desktop.Skinning.SkinnedComboBox cboItems;
 	}
 }
diff --git a/editor source/Desktop/CommonControls/PropertyControls/ComboBoxControl.cs b/editor source/Desktop/CommonControls/PropertyControls/ComboBoxControl.cs
index f80ba65dc698ef7df132ce8786d59137863d549c..952ada85bac80cb390d3edae7552aebd93ce0b2d 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/ComboBoxControl.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/ComboBoxControl.cs	
@@ -50,13 +50,13 @@ namespace Desktop.CommonControls.PropertyControls
 			cboItems.SelectedItem = GetValue()?.ToString() ?? "";
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			cboItems.SelectedItem = "";
 			Save();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			string text = cboItems.SelectedItem?.ToString();
 			if (string.IsNullOrEmpty(text))
diff --git a/editor source/Desktop/CommonControls/PropertyControls/FloatControl.cs b/editor source/Desktop/CommonControls/PropertyControls/FloatControl.cs
index 97cc323e3ed4d7d26b20d01a1566c73cfab366fb..3f3d24a00369de30cfe7e70a5224c70d009ddd55 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/FloatControl.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/FloatControl.cs	
@@ -146,7 +146,7 @@ namespace Desktop.CommonControls.PropertyControls
 			}
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			_cleared = true;
 			valValue.Value = Math.Max(valValue.Minimum, Math.Min(valValue.Maximum, 0));
@@ -154,7 +154,7 @@ namespace Desktop.CommonControls.PropertyControls
 			Save();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			float value = (float)valValue.Value;
 			SaveValue(value, valValue.Text);
diff --git a/editor source/Desktop/CommonControls/PropertyControls/NumericControl.Designer.cs b/editor source/Desktop/CommonControls/PropertyControls/NumericControl.Designer.cs
index 73dd7316a3c6b1dc8668f7cc25193df9243ea279..c44482531ae895bdc75765bb7aaa56bb2e5c2370 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/NumericControl.Designer.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/NumericControl.Designer.cs	
@@ -28,7 +28,7 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.valValue = new System.Windows.Forms.NumericUpDown();
+			this.valValue = new Desktop.Skinning.SkinnedNumericUpDown();
 			((System.ComponentModel.ISupportInitialize)(this.valValue)).BeginInit();
 			this.SuspendLayout();
 			// 
@@ -54,6 +54,6 @@
 
 		#endregion
 
-		private System.Windows.Forms.NumericUpDown valValue;
+		private Desktop.Skinning.SkinnedNumericUpDown valValue;
 	}
 }
diff --git a/editor source/Desktop/CommonControls/PropertyControls/NumericControl.cs b/editor source/Desktop/CommonControls/PropertyControls/NumericControl.cs
index 0ed5f54ec2cdb514f01522d611b15c71843d6ba1..3dfe22f26e393f6c200b0c18da56116cdc25a9e4 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/NumericControl.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/NumericControl.cs	
@@ -53,15 +53,15 @@ namespace Desktop.CommonControls.PropertyControls
 			valValue.TextChanged -= valValue_TextChanged;
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
-			valValue.Value = 0;
+			valValue.Value = valValue.Minimum;
 			valValue.Text = "";
 			_cleared = true;
 			Save();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			if (PropertyType == typeof(int?))
 			{
diff --git a/editor source/Desktop/CommonControls/PropertyControls/RecordControl.cs b/editor source/Desktop/CommonControls/PropertyControls/RecordControl.cs
index e3041fe27dacc25c03c208e9331f2ff29f01798f..28d40cbc18941ef1dead7afdca4c7a78387a0bf5 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/RecordControl.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/RecordControl.cs	
@@ -17,7 +17,12 @@ namespace Desktop.CommonControls.PropertyControls
 		{
 			if (values.Count > 0)
 			{
-				recField.RecordKey = values[0];
+				string key = values[0];
+				if (string.IsNullOrEmpty(key))
+				{
+					key = null;
+				}
+				recField.RecordKey = key;
 			}
 		}
 
@@ -64,12 +69,12 @@ namespace Desktop.CommonControls.PropertyControls
 			}
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			recField.Record = null;
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			IRecord record = recField.Record;
 			if (record == null)
diff --git a/editor source/Desktop/CommonControls/PropertyControls/RefreshableListBox.cs b/editor source/Desktop/CommonControls/PropertyControls/RefreshableListBox.cs
index 9887d909acfc49f379566e0f0a6151cde6bad563..c5b5122c4058caab5bf1daa60e24c8be29316f32 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/RefreshableListBox.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/RefreshableListBox.cs	
@@ -1,17 +1,12 @@
-using System.Windows.Forms;
+using Desktop.Skinning;
 
 namespace Desktop.CommonControls
 {
-	public class RefreshableListBox : ListBox
+	public class RefreshableListBox : SkinnedListBox
 	{
 		public RefreshableListBox() : base()
 		{
 			DoubleBuffered = true;
 		}
-
-		public void RefreshListItems()
-		{
-			this.RefreshItems();
-		}
 	}
 }
diff --git a/editor source/Desktop/CommonControls/PropertyControls/SliderControl.Designer.cs b/editor source/Desktop/CommonControls/PropertyControls/SliderControl.Designer.cs
index fe0bf5cde75bea9582526120bf12a76176e93f60..97bd4161565e286d70278e2e4541924c52ab1ce5 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/SliderControl.Designer.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/SliderControl.Designer.cs	
@@ -28,7 +28,7 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.slider = new System.Windows.Forms.TrackBar();
+			this.slider = new Desktop.Skinning.SkinnedSlider();
 			this.valValue = new Desktop.CommonControls.NumericField();
 			((System.ComponentModel.ISupportInitialize)(this.slider)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.valValue)).BeginInit();
@@ -37,27 +37,46 @@
 			// slider
 			// 
 			this.slider.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
-			this.slider.AutoSize = false;
-			this.slider.LargeChange = 10;
+			this.slider.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.slider.Increment = 10;
 			this.slider.Location = new System.Drawing.Point(0, 0);
 			this.slider.Maximum = 100;
+			this.slider.Minimum = 0;
 			this.slider.Name = "slider";
 			this.slider.Size = new System.Drawing.Size(105, 20);
 			this.slider.TabIndex = 0;
-			this.slider.TickFrequency = 10;
+			this.slider.TickFrequency = 25;
+			this.slider.Value = 0;
 			// 
 			// valValue
 			// 
 			this.valValue.Anchor = System.Windows.Forms.AnchorStyles.Right;
+			this.valValue.DecimalPlaces = 0;
 			this.valValue.Increment = new decimal(new int[] {
             10,
             0,
             0,
             0});
 			this.valValue.Location = new System.Drawing.Point(111, 0);
+			this.valValue.Maximum = new decimal(new int[] {
+            100,
+            0,
+            0,
+            0});
+			this.valValue.Minimum = new decimal(new int[] {
+            0,
+            0,
+            0,
+            0});
 			this.valValue.Name = "valValue";
+			this.valValue.PlaceholderText = "";
 			this.valValue.Size = new System.Drawing.Size(39, 20);
 			this.valValue.TabIndex = 1;
+			this.valValue.Value = new decimal(new int[] {
+            0,
+            0,
+            0,
+            0});
 			// 
 			// SliderControl
 			// 
@@ -75,7 +94,7 @@
 
 		#endregion
 
-		private System.Windows.Forms.TrackBar slider;
+		private Desktop.Skinning.SkinnedSlider slider;
 		private Desktop.CommonControls.NumericField valValue;
 	}
 }
diff --git a/editor source/Desktop/CommonControls/PropertyControls/SliderControl.cs b/editor source/Desktop/CommonControls/PropertyControls/SliderControl.cs
index df6f31453cf1890eeceaaa17f24abff916d2088d..2e1455bb7b3131f7eb3901e39e572470ed272492 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/SliderControl.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/SliderControl.cs	
@@ -77,14 +77,14 @@ namespace Desktop.CommonControls.PropertyControls
 			}
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			_cleared = true;
 			valValue.Text = "";
 			Save();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			if (DataType == typeof(int))
 			{
diff --git a/editor source/Desktop/CommonControls/PropertyControls/TextControl.cs b/editor source/Desktop/CommonControls/PropertyControls/TextControl.cs
index 6a5ad84038dbca103cad47cadcedef490f9bc733..22f8e3067cb76b42632307e0435417d767dc3396 100644
--- a/editor source/Desktop/CommonControls/PropertyControls/TextControl.cs	
+++ b/editor source/Desktop/CommonControls/PropertyControls/TextControl.cs	
@@ -8,6 +8,7 @@ namespace Desktop.CommonControls.PropertyControls
 	public partial class TextControl : PropertyEditControl
 	{
 		private string _validatorMethodName;
+		private string _formatterMethodName;
 
 		public TextControl()
 		{
@@ -19,6 +20,7 @@ namespace Desktop.CommonControls.PropertyControls
 			TextAttribute attrib = parameters as TextAttribute;
 			txtValue.Multiline = attrib.Multiline;
 			_validatorMethodName = attrib.Validator;
+			_formatterMethodName = attrib.Formatter;
 		}
 
 		public override void ApplyMacro(List<string> values)
@@ -65,7 +67,7 @@ namespace Desktop.CommonControls.PropertyControls
 			txtValue.Text = GetValue()?.ToString();
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			RemoveHandlers();
 			txtValue.Text = "";
@@ -73,9 +75,27 @@ namespace Desktop.CommonControls.PropertyControls
 			Save();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
-			SetValue(txtValue.Text);
+			string text = txtValue.Text;
+
+			if (!string.IsNullOrEmpty(_formatterMethodName))
+			{
+				MethodInfo mi = Data.GetType().GetMethod(_formatterMethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+				if (mi != null)
+				{
+					Func<string, string> formatter = (Func<string, string>)Delegate.CreateDelegate(typeof(Func<string, string>), Data, mi);
+					text = formatter(text);
+				}
+			}
+			if (string.IsNullOrEmpty(text))
+			{
+				SetValue(null);
+			}
+			else
+			{
+				SetValue(text);
+			}
 		}
 
 		private void txtValue_TextChanged(object sender, System.EventArgs e)
@@ -115,6 +135,7 @@ namespace Desktop.CommonControls.PropertyControls
 
 		public bool Multiline;
 		public string Validator;
+		public string Formatter;
 	}
 
 	public interface IAutoCompleteList
diff --git a/editor source/Desktop/CommonControls/PropertyEditControl.cs b/editor source/Desktop/CommonControls/PropertyEditControl.cs
index 088d161d65811eb5f33cc14c03396aeab82bfd4a..98532a8dcc41ca82f66841b2bc37469fec90bd35 100644
--- a/editor source/Desktop/CommonControls/PropertyEditControl.cs	
+++ b/editor source/Desktop/CommonControls/PropertyEditControl.cs	
@@ -26,12 +26,22 @@ namespace Desktop.CommonControls
 		public List<string> Bindings = new List<string>();
 		private INotifyPropertyChanged _bindableData;
 		private INotifyPropertyChanged _bindablePreviewData;
-		private bool _selfUpdating;
+		public PropertyTable ParentTable;
 
 		public Type DataType { get { return _propertyInfo.GetDataType(); } }
 
 		public string Property { get; set; }
-		public int Index { get; set; }
+		private int _index;
+		public int Index
+		{
+			get { return _index; }
+			set
+			{
+				_index = value;
+				OnIndexChanged();
+			}
+		}
+		protected virtual void OnIndexChanged() { }
 
 		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly")]
 		public event EventHandler<int> RequireHeight;
@@ -82,10 +92,18 @@ namespace Desktop.CommonControls
 
 		internal void RemoveFromList()
 		{
+			string old = ParentTable.ModifyingProperty;
+			ParentTable.ModifyingProperty = Property;
 			if (_list != null)
 			{
 				_list.RemoveAt(Index);
 			}
+			ParentTable.ModifyingProperty = old;
+		}
+
+		protected IList GetList()
+		{
+			return _list;
 		}
 
 		protected object GetValue()
@@ -129,7 +147,8 @@ namespace Desktop.CommonControls
 			}
 
 			//update the data either directly if no UndoManager is present, or through the UndoManager if one is
-			_selfUpdating = true;
+			string oldProp = ParentTable.ModifyingProperty;
+			ParentTable.ModifyingProperty = Property;
 			if (UndoManager != null)
 			{
 				SetValueCommand command = new SetValueCommand(Data, this, old, value);
@@ -139,7 +158,7 @@ namespace Desktop.CommonControls
 			{
 				SetValueDirectly(Data, value);
 			}
-			_selfUpdating = false;
+			ParentTable.ModifyingProperty = oldProp;
 		}
 		internal void SetValueDirectly(object data, object value)
 		{
@@ -223,8 +242,9 @@ namespace Desktop.CommonControls
 		/// </summary>
 		/// <param name="data"></param>
 		/// <param name="property"></param>
-		public void SetData(object data, string property, int index, object context, UndoManager undoManager, object previewData)
+		public void SetData(object data, string property, int index, object context, UndoManager undoManager, object previewData, PropertyTable table)
 		{
+			ParentTable = table;
 			SetBindableData(data, ref _bindableData);
 			SetBindableData(previewData, ref _bindablePreviewData);
 			UndoManager = undoManager;
@@ -246,11 +266,20 @@ namespace Desktop.CommonControls
 			UpdateBinding(false);
 		}
 
+		public virtual bool IsUpdating
+		{
+			get
+			{
+				return ParentTable.ModifyingProperty == Property;
+			}
+		}
+
 		/// <summary>
 		/// Called when the property behind this control is either first set or updated externally
 		/// </summary>
 		private void UpdateBinding(bool rebind)
 		{
+			if (IsUpdating) { return; }
 			if (rebind)
 			{
 				RemoveHandlers();
@@ -270,7 +299,7 @@ namespace Desktop.CommonControls
 
 		private void BindableData_PropertyChanged(object sender, PropertyChangedEventArgs e)
 		{
-			if (_selfUpdating || e.PropertyName != Property)
+			if (IsUpdating || e.PropertyName != Property || _list != null) //auto-rebinding disabled for lists since the element's controls interfere with each other
 			{
 				return;
 			}
@@ -300,15 +329,29 @@ namespace Desktop.CommonControls
 			UpdateBinding(true);
 		}
 
+		public void Clear()
+		{
+			string old = ParentTable.ModifyingProperty;
+			ParentTable.ModifyingProperty = Property;
+			OnClear();
+			ParentTable.ModifyingProperty = old;
+		}
 		/// <summary>
 		/// Clears the backing field
 		/// </summary>
-		public virtual void Clear() { }
+		protected virtual void OnClear() { }
 
+		public void Save()
+		{
+			string old = ParentTable.ModifyingProperty;
+			ParentTable.ModifyingProperty = Property;
+			OnSave();
+			ParentTable.ModifyingProperty = old;
+		}
 		/// <summary>
 		/// Saves the current data
 		/// </summary>
-		public virtual void Save() { }
+		protected virtual void OnSave() { }
 
 		private class SetValueCommand : ICommand
 		{
diff --git a/editor source/Desktop/CommonControls/PropertyTable.Designer.cs b/editor source/Desktop/CommonControls/PropertyTable.Designer.cs
index e759c05a263c4d9eada79311f809e36bf8d6cee8..c0e814ce65f551582bd79b59b3c60521cd4b3905 100644
--- a/editor source/Desktop/CommonControls/PropertyTable.Designer.cs	
+++ b/editor source/Desktop/CommonControls/PropertyTable.Designer.cs	
@@ -28,40 +28,14 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.recAdd = new Desktop.CommonControls.RecordField();
-			this.pnlRecords = new Desktop.CommonControls.DBPanel();
 			this.shortcuts = new System.Windows.Forms.MenuStrip();
 			this.focusOnAdd = new System.Windows.Forms.ToolStripMenuItem();
-			this.menuSpeedButtons = new System.Windows.Forms.MenuStrip();
+			this.menuSpeedButtons = new Desktop.Skinning.SkinnedMenuStrip();
+			this.recAdd = new Desktop.CommonControls.RecordField();
+			this.pnlRecords = new Desktop.CommonControls.DBPanel();
 			this.shortcuts.SuspendLayout();
 			this.SuspendLayout();
 			// 
-			// recAdd
-			// 
-			this.recAdd.AllowCreate = false;
-			this.recAdd.Location = new System.Drawing.Point(3, 3);
-			this.recAdd.Name = "recAdd";
-			this.recAdd.PlaceholderText = null;
-			this.recAdd.Record = null;
-			this.recAdd.RecordContext = null;
-			this.recAdd.RecordKey = null;
-			this.recAdd.RecordType = null;
-			this.recAdd.Size = new System.Drawing.Size(138, 20);
-			this.recAdd.TabIndex = 0;
-			this.recAdd.UseAutoComplete = false;
-			this.recAdd.RecordChanged += new System.EventHandler<Desktop.CommonControls.RecordEventArgs>(this.recAdd_RecordChanged);
-			// 
-			// pnlRecords
-			// 
-			this.pnlRecords.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.pnlRecords.AutoScroll = true;
-			this.pnlRecords.Location = new System.Drawing.Point(3, 27);
-			this.pnlRecords.Name = "pnlRecords";
-			this.pnlRecords.Size = new System.Drawing.Size(857, 120);
-			this.pnlRecords.TabIndex = 2;
-			// 
 			// shortcuts
 			// 
 			this.shortcuts.Dock = System.Windows.Forms.DockStyle.Bottom;
@@ -85,11 +59,41 @@
 			// menuSpeedButtons
 			// 
 			this.menuSpeedButtons.AutoSize = false;
+			this.menuSpeedButtons.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.menuSpeedButtons.Dock = System.Windows.Forms.DockStyle.None;
 			this.menuSpeedButtons.Location = new System.Drawing.Point(142, 0);
 			this.menuSpeedButtons.Name = "menuSpeedButtons";
 			this.menuSpeedButtons.Size = new System.Drawing.Size(721, 24);
 			this.menuSpeedButtons.TabIndex = 4;
+			this.menuSpeedButtons.Tag = "Surface";
+			// 
+			// recAdd
+			// 
+			this.recAdd.AllowCreate = false;
+			this.recAdd.Location = new System.Drawing.Point(3, 3);
+			this.recAdd.Name = "recAdd";
+			this.recAdd.PlaceholderText = null;
+			this.recAdd.Record = null;
+			this.recAdd.RecordContext = null;
+			this.recAdd.RecordFilter = null;
+			this.recAdd.RecordKey = null;
+			this.recAdd.RecordType = null;
+			this.recAdd.Size = new System.Drawing.Size(138, 20);
+			this.recAdd.TabIndex = 0;
+			this.recAdd.UseAutoComplete = false;
+			this.recAdd.RecordChanged += new System.EventHandler<Desktop.CommonControls.RecordEventArgs>(this.recAdd_RecordChanged);
+			// 
+			// pnlRecords
+			// 
+			this.pnlRecords.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.pnlRecords.AutoScroll = true;
+			this.pnlRecords.Location = new System.Drawing.Point(3, 27);
+			this.pnlRecords.Name = "pnlRecords";
+			this.pnlRecords.PanelType = Desktop.Skinning.SkinnedBackgroundType.Transparent;
+			this.pnlRecords.Size = new System.Drawing.Size(860, 120);
+			this.pnlRecords.TabIndex = 2;
 			// 
 			// PropertyTable
 			// 
@@ -114,6 +118,6 @@
 		private DBPanel pnlRecords;
 		private System.Windows.Forms.MenuStrip shortcuts;
 		private System.Windows.Forms.ToolStripMenuItem focusOnAdd;
-		private System.Windows.Forms.MenuStrip menuSpeedButtons;
+		private Desktop.Skinning.SkinnedMenuStrip menuSpeedButtons;
 	}
 }
diff --git a/editor source/Desktop/CommonControls/PropertyTable.cs b/editor source/Desktop/CommonControls/PropertyTable.cs
index 062bc287ac3627f7ae67af5f273db1eb7a76fe76..d5b33d1eaf4898f9be009dfc91f99a8612b5b47c 100644
--- a/editor source/Desktop/CommonControls/PropertyTable.cs	
+++ b/editor source/Desktop/CommonControls/PropertyTable.cs	
@@ -1,5 +1,6 @@
 using Desktop.Forms;
 using Desktop.Providers;
+using Desktop.Skinning;
 using System;
 using System.Collections;
 using System.Collections.Generic;
@@ -11,7 +12,7 @@ using System.Windows.Forms;
 
 namespace Desktop.CommonControls
 {
-	public partial class PropertyTable : UserControl
+	public partial class PropertyTable : UserControl, ISkinControl, ISkinnedPanel
 	{
 		/// <summary>
 		/// Contextual object that will be passed to edit controls
@@ -22,6 +23,19 @@ namespace Desktop.CommonControls
 		public event EventHandler<MacroArgs> EditingMacro;
 		public event EventHandler<MacroArgs> MacroChanged;
 
+		private SkinnedBackgroundType _background;
+		public SkinnedBackgroundType PanelType
+		{
+			get { return _background; }
+			set { _background = value; OnUpdateSkin(SkinManager.Instance.CurrentSkin); Invalidate(true); }
+		}
+
+		public SkinnedBackgroundType HeaderType
+		{
+			get { return menuSpeedButtons.Background; }
+			set { menuSpeedButtons.Background = value; }
+		}
+
 		private int _pendingDataChanges = 0;
 
 		private object _data;
@@ -203,6 +217,8 @@ namespace Desktop.CommonControls
 
 		private DualKeyDictionary<string, int, PropertyTableRow> _rows = new DualKeyDictionary<string, int, PropertyTableRow>();
 
+		public string ModifyingProperty { get; set; }
+
 		public PropertyTable()
 		{
 			RemoveCaption = "Remove";
@@ -210,6 +226,8 @@ namespace Desktop.CommonControls
 
 			recAdd.RecordFilter = FilterControlsToData;
 			recAdd.RecordType = typeof(PropertyRecord);
+
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
 		}
 
 		/// <summary>
@@ -550,7 +568,7 @@ namespace Desktop.CommonControls
 				ctl.SetParameters(result.Attribute);
 				ctl.RequireHeight += PropertyEditControl_RequireHeight;
 
-				ctl.SetData(Data, result.Property, index, Context, UndoManager, _previewData);
+				ctl.SetData(Data, result.Property, index, Context, UndoManager, _previewData, this);
 
 				row = new PropertyTableRow();
 				if (result.RowHeight > 0)
@@ -692,7 +710,7 @@ namespace Desktop.CommonControls
 			if (!_rows.ContainsPrimaryKey(propertyName))
 			{
 				PropertyRecord record = PropertyProvider.GetEditControls(Data.GetType()).FirstOrDefault(r => r.Property == propertyName);
-				if (RecordFilter == null || RecordFilter(record))
+				if (record != null && (RecordFilter == null || RecordFilter(record)))
 				{
 					AddControl(record);
 				}
@@ -780,10 +798,16 @@ namespace Desktop.CommonControls
 
 		private ToolStripMenuItem GetOrAddGroupMenu(string group)
 		{
+			int macroSeparator = -1;
 			ToolStripMenuItem groupMenu = null;
 			for (int i = 0; i < menuSpeedButtons.Items.Count; i++)
 			{
 				ToolStripItem mnuItem = menuSpeedButtons.Items[i];
+				if (mnuItem is ToolStripSeparator)
+				{
+					macroSeparator = i;
+					continue;
+				}
 				if (mnuItem.Text == group)
 				{
 					groupMenu = mnuItem as ToolStripMenuItem;
@@ -793,7 +817,18 @@ namespace Desktop.CommonControls
 			if (groupMenu == null)
 			{
 				groupMenu = new ToolStripMenuItem(group);
-				menuSpeedButtons.Items.Add(groupMenu);
+				if (macroSeparator >= 0)
+				{
+					menuSpeedButtons.Items.Insert(macroSeparator, groupMenu);
+				}
+				else
+				{
+					if (group == "Macros")
+					{
+						menuSpeedButtons.Items.Add(new ToolStripSeparator());
+					}
+					menuSpeedButtons.Items.Add(groupMenu);
+				}
 			}
 			return groupMenu;
 		}
@@ -805,6 +840,17 @@ namespace Desktop.CommonControls
 			ToolStripMenuItem item = new ToolStripMenuItem(caption);
 			item.Tag = propertyCreator;
 			item.Click += CustomSpeedButtonClick;
+
+			//insert before the macro separator if there is one
+			for (int i = 0; i < groupMenu.DropDownItems.Count; i++)
+			{
+				if (groupMenu.DropDownItems[i] is ToolStripSeparator)
+				{
+					groupMenu.DropDownItems.Insert(i, item);
+					return;
+				}
+			}
+
 			groupMenu.DropDownItems.Add(item);
 		}
 
@@ -855,12 +901,47 @@ namespace Desktop.CommonControls
 				}
 			}
 
-			ToolStripMenuItem groupMenu = GetOrAddGroupMenu("Macros");
+			string group = macro.Group;
+			if (string.IsNullOrEmpty(group))
+			{
+				group = "Macros";
+			}
+			ToolStripMenuItem groupMenu = GetOrAddGroupMenu(group);
 			_macroMenu = groupMenu;
 			ToolStripMenuItem item = new ToolStripMenuItem(macro.Name);
 			item.Tag = macro;
 			item.Click += MacroButtonClick;
-			groupMenu.DropDownItems.Add(item);
+
+			bool added = false;
+			int macroSeparator = -1;
+			for (int i = 0; i < groupMenu.DropDownItems.Count; i++)
+			{
+				ToolStripSeparator separator = groupMenu.DropDownItems[i] as ToolStripSeparator;
+				if (separator != null)
+				{
+					macroSeparator = i;
+					break;
+				}
+				if (macroSeparator >= 0 || group == "Macros")
+				{
+					ToolStripItem existing = groupMenu.DropDownItems[i];
+					if (existing.Name.CompareTo(macro.Name) > 0)
+					{
+						added = true;
+						groupMenu.DropDownItems.Insert(i, item);
+						break;
+					}
+				}
+			}
+
+			if (!added)
+			{
+				if (macroSeparator == -1 && group != "Macros")
+				{
+					groupMenu.DropDownItems.Add(new ToolStripSeparator());
+				}
+				groupMenu.DropDownItems.Add(item);
+			}
 		}
 
 		private void MacroButtonClick(object sender, EventArgs e)
@@ -917,5 +998,10 @@ namespace Desktop.CommonControls
 			}
 			return macro;
 		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			BackColor = skin.GetBackColor(PanelType);
+		}
 	}
 }
diff --git a/editor source/Desktop/CommonControls/PropertyTableRow.Designer.cs b/editor source/Desktop/CommonControls/PropertyTableRow.Designer.cs
index 4a39043bce94437cb857851bf066e68ad2b2fe78..84b032509caa7f94f6e8803aea93a5a6297d23b9 100644
--- a/editor source/Desktop/CommonControls/PropertyTableRow.Designer.cs	
+++ b/editor source/Desktop/CommonControls/PropertyTableRow.Designer.cs	
@@ -30,10 +30,10 @@
 		{
 			this.components = new System.ComponentModel.Container();
 			this.table = new System.Windows.Forms.TableLayoutPanel();
-			this.lblName = new System.Windows.Forms.Label();
+			this.lblName = new Desktop.Skinning.SkinnedLabel();
 			this.cmdPin = new System.Windows.Forms.Button();
 			this.cmdRemove = new System.Windows.Forms.Button();
-			this.lblHelp = new System.Windows.Forms.Label();
+			this.lblHelp = new Desktop.Skinning.SkinnedLabel();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
 			this.menuFavorite = new System.Windows.Forms.ContextMenuStrip(this.components);
 			this.itemFavorite = new System.Windows.Forms.ToolStripMenuItem();
@@ -45,7 +45,7 @@
 			// table
 			// 
 			this.table.ColumnCount = 5;
-			this.table.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+			this.table.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 22F));
 			this.table.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 150F));
 			this.table.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
 			this.table.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 26F));
@@ -65,7 +65,11 @@
 			// lblName
 			// 
 			this.lblName.AutoSize = true;
-			this.lblName.Location = new System.Drawing.Point(23, 7);
+			this.lblName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblName.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.lblName.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.lblName.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblName.Location = new System.Drawing.Point(25, 7);
 			this.lblName.Margin = new System.Windows.Forms.Padding(3, 7, 3, 0);
 			this.lblName.Name = "lblName";
 			this.lblName.Size = new System.Drawing.Size(35, 13);
@@ -105,11 +109,15 @@
 			// 
 			// lblHelp
 			// 
+			this.lblHelp.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblHelp.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.lblHelp.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
 			this.lblHelp.Image = global::Desktop.Properties.Resources.Help;
+			this.lblHelp.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.lblHelp.Location = new System.Drawing.Point(3, 3);
 			this.lblHelp.Margin = new System.Windows.Forms.Padding(3);
 			this.lblHelp.Name = "lblHelp";
-			this.lblHelp.Size = new System.Drawing.Size(14, 22);
+			this.lblHelp.Size = new System.Drawing.Size(16, 22);
 			this.lblHelp.TabIndex = 4;
 			// 
 			// menuFavorite
@@ -118,7 +126,7 @@
             this.itemFavorite,
             this.itemMacro});
 			this.menuFavorite.Name = "menuFavorite";
-			this.menuFavorite.Size = new System.Drawing.Size(159, 70);
+			this.menuFavorite.Size = new System.Drawing.Size(159, 48);
 			// 
 			// itemFavorite
 			// 
@@ -152,11 +160,11 @@
 		#endregion
 
 		private System.Windows.Forms.TableLayoutPanel table;
-		private System.Windows.Forms.Label lblName;
+		private Desktop.Skinning.SkinnedLabel lblName;
 		private System.Windows.Forms.Button cmdRemove;
 		private System.Windows.Forms.ToolTip toolTip1;
 		private System.Windows.Forms.Button cmdPin;
-		private System.Windows.Forms.Label lblHelp;
+		private Desktop.Skinning.SkinnedLabel lblHelp;
 		private System.Windows.Forms.ContextMenuStrip menuFavorite;
 		private System.Windows.Forms.ToolStripMenuItem itemFavorite;
 		private System.Windows.Forms.ToolStripMenuItem itemMacro;
diff --git a/editor source/Desktop/CommonControls/PropertyTableRow.cs b/editor source/Desktop/CommonControls/PropertyTableRow.cs
index 6d97fab6f59542728f9f6825d22c303292078bc2..ff8bb1aada9e7fc6900d1f2bd021410bbb19fb49 100644
--- a/editor source/Desktop/CommonControls/PropertyTableRow.cs	
+++ b/editor source/Desktop/CommonControls/PropertyTableRow.cs	
@@ -1,5 +1,4 @@
-using Desktop.Providers;
-using System;
+using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Windows.Forms;
@@ -46,6 +45,7 @@ namespace Desktop.CommonControls
 			{
 				_helpWidth = table.ColumnStyles[0].Width;
 				_allowHelp = value;
+				lblHelp.Visible = value;
 				if (!_allowHelp)
 				{
 					table.ColumnStyles[0].Width = 0;
diff --git a/editor source/Desktop/CommonControls/PropertyTableRow.resx b/editor source/Desktop/CommonControls/PropertyTableRow.resx
index b6de39ddbd2f4a57e39f21231a4a63bc21892567..931973c398ae4547762738767173a2bca7126fff 100644
--- a/editor source/Desktop/CommonControls/PropertyTableRow.resx	
+++ b/editor source/Desktop/CommonControls/PropertyTableRow.resx	
@@ -120,9 +120,6 @@
   <metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>17, 17</value>
   </metadata>
-  <metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>17, 17</value>
-  </metadata>
   <metadata name="menuFavorite.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>114, 17</value>
   </metadata>
diff --git a/editor source/Desktop/CommonControls/RecordField.Designer.cs b/editor source/Desktop/CommonControls/RecordField.Designer.cs
index d7ae95bbe92e66b9118bcf42d0e43e50cc19bda8..735137e3d32272b0507a2879093791383e3b1b87 100644
--- a/editor source/Desktop/CommonControls/RecordField.Designer.cs	
+++ b/editor source/Desktop/CommonControls/RecordField.Designer.cs	
@@ -28,18 +28,22 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.cmdSearch = new System.Windows.Forms.Button();
-			this.txtInput = new System.Windows.Forms.TextBox();
+			this.cmdSearch = new Desktop.Skinning.SkinnedIcon();
+			this.txtInput = new Desktop.Skinning.SkinnedTextBox();
 			this.SuspendLayout();
 			// 
 			// cmdSearch
 			// 
-			this.cmdSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdSearch.Anchor = System.Windows.Forms.AnchorStyles.Right;
+			this.cmdSearch.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdSearch.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cmdSearch.Flat = true;
+			this.cmdSearch.ForeColor = System.Drawing.Color.Black;
 			this.cmdSearch.Image = global::Desktop.Properties.Resources.Search;
-			this.cmdSearch.Location = new System.Drawing.Point(129, -1);
+			this.cmdSearch.Location = new System.Drawing.Point(131, 1);
 			this.cmdSearch.Margin = new System.Windows.Forms.Padding(0);
 			this.cmdSearch.Name = "cmdSearch";
-			this.cmdSearch.Size = new System.Drawing.Size(22, 22);
+			this.cmdSearch.Size = new System.Drawing.Size(18, 18);
 			this.cmdSearch.TabIndex = 3;
 			this.cmdSearch.UseVisualStyleBackColor = true;
 			this.cmdSearch.Click += new System.EventHandler(this.cmdSearch_Click);
@@ -51,10 +55,14 @@
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.txtInput.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
 			this.txtInput.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
-			this.txtInput.Location = new System.Drawing.Point(0, 0);
+			this.txtInput.BackColor = System.Drawing.Color.White;
+			this.txtInput.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.txtInput.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtInput.ForeColor = System.Drawing.Color.Black;
+			this.txtInput.Location = new System.Drawing.Point(3, 3);
 			this.txtInput.Margin = new System.Windows.Forms.Padding(0);
 			this.txtInput.Name = "txtInput";
-			this.txtInput.Size = new System.Drawing.Size(129, 20);
+			this.txtInput.Size = new System.Drawing.Size(126, 13);
 			this.txtInput.TabIndex = 2;
 			this.txtInput.TextChanged += new System.EventHandler(this.txtInput_TextChanged);
 			this.txtInput.Enter += new System.EventHandler(this.txtInput_Enter);
@@ -79,7 +87,7 @@
 
 		#endregion
 
-		private System.Windows.Forms.Button cmdSearch;
-		private System.Windows.Forms.TextBox txtInput;
+		private Desktop.Skinning.SkinnedIcon cmdSearch;
+		private Desktop.Skinning.SkinnedTextBox txtInput;
 	}
 }
diff --git a/editor source/Desktop/CommonControls/RecordField.cs b/editor source/Desktop/CommonControls/RecordField.cs
index 8699fb4e66f43ff40b404ac247e052e1f5225aa4..d517b2a1afd27995c81b6ad487d9bb3575fa043a 100644
--- a/editor source/Desktop/CommonControls/RecordField.cs	
+++ b/editor source/Desktop/CommonControls/RecordField.cs	
@@ -1,4 +1,5 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Collections.Generic;
 using System.Drawing;
 using System.Linq;
@@ -6,7 +7,7 @@ using System.Windows.Forms;
 
 namespace Desktop.CommonControls
 {
-	public partial class RecordField : UserControl
+	public partial class RecordField : UserControl, ISkinnedPanel
 	{
 		public event EventHandler<RecordEventArgs> RecordChanged;
 
@@ -40,10 +41,19 @@ namespace Desktop.CommonControls
 		private bool _selectAllDone = false;
 		private bool _populatingRecord = false;
 
+		private Func<IRecord, bool> _filter;
 		/// <summary>
 		/// Filter for hiding records from the record select
 		/// </summary>
-		public Func<IRecord, bool> RecordFilter;
+		public Func<IRecord, bool> RecordFilter
+		{
+			get { return _filter; }
+			set
+			{
+				_filter = value;
+				PopulateAutoComplete();
+			}
+		}
 
 		private IRecord _record;
 		public IRecord Record
@@ -64,13 +74,26 @@ namespace Desktop.CommonControls
 					{
 						txtInput.Text = "";
 					}
-					txtInput.ForeColor = ForeColor;
+					txtInput.ForeColor = SkinManager.Instance.CurrentSkin.Surface.ForeColor;
 					RecordChanged?.Invoke(this, new RecordEventArgs(_record));
+					OnRecordChanged();
 					_populatingRecord = false;
 				}
 			}
 		}
 
+		protected override void OnPaintBackground(PaintEventArgs e)
+		{
+			base.OnPaintBackground(e);
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			e.Graphics.Clear(Enabled ? skin.FieldBackColor : SystemColors.Control);
+			e.Graphics.DrawRectangle(skin.PrimaryColor.GetPen(VisualState.Normal, Focused, Enabled), ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width - 1, ClientRectangle.Height - 1);
+		}
+
+		protected virtual void OnRecordChanged()
+		{
+		}
+
 		public string RecordKey
 		{
 			get { return _record?.Key; }
@@ -134,6 +157,11 @@ namespace Desktop.CommonControls
 			}
 		}
 
+		public SkinnedBackgroundType PanelType
+		{
+			get { return SkinnedBackgroundType.Field; }
+		}
+
 		public RecordField()
 		{
 			InitializeComponent();
@@ -146,12 +174,26 @@ namespace Desktop.CommonControls
 			SetPlaceholder();
 		}
 
-		private void RecordField_Validating(object sender, System.ComponentModel.CancelEventArgs e)
+		private string GetText()
 		{
-			if (_needValidation && txtInput.Text != "" && txtInput.Text != PlaceholderText)
+			string text = txtInput.Text;
+			int bracketStart = text.IndexOf('[');
+			int bracketEnd = text.IndexOf(']');
+			if (bracketStart >= 0 && bracketEnd > bracketStart)
 			{
-				DoSearch(false);
+				text = text.Substring(bracketStart + 1, bracketEnd - bracketStart - 1);
 			}
+			return text;
+		}
+
+		private void RecordField_Validating(object sender, System.ComponentModel.CancelEventArgs e)
+		{
+			ValidateField();
+		}
+
+		public void SelectAll()
+		{
+			txtInput.SelectAll();
 		}
 
 		private void txtInput_Enter(object sender, EventArgs e)
@@ -220,7 +262,7 @@ namespace Desktop.CommonControls
 			if (txtInput.Text == "")
 			{
 				txtInput.Text = PlaceholderText;
-				txtInput.ForeColor = Color.Gray;
+				txtInput.ForeColor = SkinManager.Instance.CurrentSkin.Gray;
 			}
 		}
 
@@ -230,7 +272,16 @@ namespace Desktop.CommonControls
 			if (txtInput.Text == PlaceholderText && !string.IsNullOrEmpty(PlaceholderText))
 			{
 				txtInput.Text = "";
-				txtInput.ForeColor = ForeColor;
+				txtInput.ForeColor = SkinManager.Instance.CurrentSkin.Surface.ForeColor;
+			}
+		}
+
+		public void ValidateField()
+		{
+			string text = GetText();
+			if (_needValidation && text != "" && text != PlaceholderText)
+			{
+				DoSearch(false);				
 			}
 		}
 
@@ -239,11 +290,12 @@ namespace Desktop.CommonControls
 			DoSearch(true);
 		}
 
-		private void DoSearch(bool forceLookup, string text = null)
+		protected void DoSearch(bool forceLookup, string text = null)
 		{
+			if (RecordLookup.IsOpen) { return; }
 			if (text == null)
 			{
-				text = txtInput.Text;
+				text = GetText();
 				if (text == PlaceholderText)
 				{
 					text = "";
diff --git a/editor source/Desktop/CommonControls/Slider.cs b/editor source/Desktop/CommonControls/Slider.cs
deleted file mode 100644
index 203414811f40b7dda99f8223f5e68491346844b3..0000000000000000000000000000000000000000
--- a/editor source/Desktop/CommonControls/Slider.cs	
+++ /dev/null
@@ -1,62 +0,0 @@
-using System;
-using System.Windows.Forms;
-
-namespace Desktop.CommonControls
-{
-	public partial class Slider : UserControl
-	{
-		public Slider()
-		{
-			InitializeComponent();
-		}
-
-		public string Label
-		{
-			get { return lblLabel.Text; }
-			set { lblLabel.Text = value; }
-		}
-
-		public int Value
-		{
-			get { return trackbar.Value; }
-			set { trackbar.Value = value; }
-		}
-
-		public int TickFrequency
-		{
-			get { return trackbar.TickFrequency; }
-			set { trackbar.TickFrequency = value; }
-		}
-
-		public int Maximum
-		{
-			get { return trackbar.Maximum; }
-			set { trackbar.Maximum = value; }
-		}
-
-		public int Minimum
-		{
-			get { return trackbar.Minimum; }
-			set { trackbar.Minimum = value; }
-		}
-
-		public int SmallChange
-		{
-			get { return trackbar.SmallChange; }
-			set { trackbar.SmallChange = value; }
-		}
-
-		public int LargeChange
-		{
-			get { return trackbar.LargeChange; }
-			set { trackbar.LargeChange = value; }
-		}
-
-		public event EventHandler ValueChanged;
-		private void trackbar_ValueChanged(object sender, EventArgs e)
-		{
-			lblValue.Text = trackbar.Value.ToString();
-			ValueChanged?.Invoke(this, e);
-		}
-	}
-}
diff --git a/editor source/Desktop/CommonControls/Slider.designer.cs b/editor source/Desktop/CommonControls/Slider.designer.cs
deleted file mode 100644
index e0d8f5caa06b05b35790804dc3c38bff92bc9bba..0000000000000000000000000000000000000000
--- a/editor source/Desktop/CommonControls/Slider.designer.cs	
+++ /dev/null
@@ -1,90 +0,0 @@
-namespace Desktop.CommonControls
-{
-	partial class Slider
-	{
-		/// <summary> 
-		/// Required designer variable.
-		/// </summary>
-		private System.ComponentModel.IContainer components = null;
-
-		/// <summary> 
-		/// Clean up any resources being used.
-		/// </summary>
-		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
-		protected override void Dispose(bool disposing)
-		{
-			if (disposing && (components != null))
-			{
-				components.Dispose();
-			}
-			base.Dispose(disposing);
-		}
-
-		#region Component Designer generated code
-
-		/// <summary> 
-		/// Required method for Designer support - do not modify 
-		/// the contents of this method with the code editor.
-		/// </summary>
-		private void InitializeComponent()
-		{
-			this.lblLabel = new System.Windows.Forms.Label();
-			this.trackbar = new System.Windows.Forms.TrackBar();
-			this.lblValue = new System.Windows.Forms.Label();
-			((System.ComponentModel.ISupportInitialize)(this.trackbar)).BeginInit();
-			this.SuspendLayout();
-			// 
-			// lblLabel
-			// 
-			this.lblLabel.AutoSize = true;
-			this.lblLabel.Location = new System.Drawing.Point(3, 8);
-			this.lblLabel.Name = "lblLabel";
-			this.lblLabel.Size = new System.Drawing.Size(36, 13);
-			this.lblLabel.TabIndex = 0;
-			this.lblLabel.Text = "Label:";
-			// 
-			// trackbar
-			// 
-			this.trackbar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.trackbar.LargeChange = 10;
-			this.trackbar.Location = new System.Drawing.Point(72, 3);
-			this.trackbar.Maximum = 100;
-			this.trackbar.Name = "trackbar";
-			this.trackbar.Size = new System.Drawing.Size(324, 45);
-			this.trackbar.TabIndex = 1;
-			this.trackbar.TickFrequency = 10;
-			this.trackbar.ValueChanged += new System.EventHandler(this.trackbar_ValueChanged);
-			// 
-			// lblValue
-			// 
-			this.lblValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblValue.AutoSize = true;
-			this.lblValue.Location = new System.Drawing.Point(396, 11);
-			this.lblValue.Name = "lblValue";
-			this.lblValue.Size = new System.Drawing.Size(13, 13);
-			this.lblValue.TabIndex = 2;
-			this.lblValue.Text = "0";
-			// 
-			// Slider
-			// 
-			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.lblValue);
-			this.Controls.Add(this.trackbar);
-			this.Controls.Add(this.lblLabel);
-			this.Name = "Slider";
-			this.Size = new System.Drawing.Size(424, 37);
-			((System.ComponentModel.ISupportInitialize)(this.trackbar)).EndInit();
-			this.ResumeLayout(false);
-			this.PerformLayout();
-
-		}
-
-		#endregion
-
-		private System.Windows.Forms.Label lblLabel;
-		private System.Windows.Forms.TrackBar trackbar;
-		private System.Windows.Forms.Label lblValue;
-	}
-}
diff --git a/editor source/Desktop/CommonControls/TextField.Designer.cs b/editor source/Desktop/CommonControls/TextField.Designer.cs
index 8503463eb5c874afd5692227ebd92f3194f79520..1647db92b4d471be639707dbf9552f71e5e4f568 100644
--- a/editor source/Desktop/CommonControls/TextField.Designer.cs	
+++ b/editor source/Desktop/CommonControls/TextField.Designer.cs	
@@ -28,7 +28,7 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.txtField = new System.Windows.Forms.TextBox();
+			this.txtField = new Desktop.Skinning.SkinnedTextBox();
 			this.lblPlaceholder = new System.Windows.Forms.Label();
 			this.SuspendLayout();
 			// 
@@ -71,7 +71,7 @@
 
 		#endregion
 
-		private System.Windows.Forms.TextBox txtField;
+		private Desktop.Skinning.SkinnedTextBox txtField;
 		private System.Windows.Forms.Label lblPlaceholder;
 	}
 }
diff --git a/editor source/Desktop/CommonControls/TextField.cs b/editor source/Desktop/CommonControls/TextField.cs
index 61ce15a9e521ffdd72ed085875f2b203d6881930..13bcca4127a78d8f65355aa732253fb926785d80 100644
--- a/editor source/Desktop/CommonControls/TextField.cs	
+++ b/editor source/Desktop/CommonControls/TextField.cs	
@@ -1,9 +1,10 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Windows.Forms;
 
 namespace Desktop.CommonControls
 {
-	public partial class TextField : UserControl
+	public partial class TextField : UserControl, ISkinControl
 	{
 		private bool _selectAllDone;
 
@@ -130,5 +131,11 @@ namespace Desktop.CommonControls
 				_selectAllDone = false;
 			}
 		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			lblPlaceholder.BackColor = skin.FieldBackColor;
+			lblPlaceholder.ForeColor = skin.Surface.DisabledForeColor;
+		}
 	}
 }
diff --git a/editor source/Desktop/Controls/Activity.cs b/editor source/Desktop/Controls/Activity.cs
index e705feaf1281b58f2814603ac638d4af869b5c37..eb163036ceb7e5d709a8146743c118cd19c76d82 100644
--- a/editor source/Desktop/Controls/Activity.cs	
+++ b/editor source/Desktop/Controls/Activity.cs	
@@ -2,11 +2,12 @@
 using System.Windows.Forms;
 using System.ComponentModel;
 using Desktop.Messaging;
+using Desktop.Skinning;
 
 namespace Desktop
 {
 	[ToolboxItem(false)]
-	public partial class Activity : UserControl, IActivity
+	public partial class Activity : UserControl, IActivity, ISkinControl, ISkinnedPanel
 	{
 		public IWorkspace Workspace { get; set; }
 		public WorkspacePane Pane { get; set; }
@@ -28,6 +29,7 @@ namespace Desktop
 			_workspaceMailbox = ws.GetMailbox();
 			_desktopMailbox = Shell.Instance.PostOffice.GetMailbox();
 
+			SkinManager.UpdateSkin(this, SkinManager.Instance.CurrentSkin);
 			OnInitialize();
 		}
 		protected virtual void OnInitialize()
@@ -66,6 +68,11 @@ namespace Desktop
 			get { return Workspace.ActiveActivity == this; }
 		}
 
+		public SkinnedBackgroundType PanelType
+		{
+			get { return SkinnedBackgroundType.Background; }
+		}
+
 		protected virtual void OnFirstActivate()
 		{
 		}
@@ -127,5 +134,15 @@ namespace Desktop
 		{
 			_workspaceMailbox.Subscribe(message, handler);
 		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			BackColor = skin.Background.Normal;
+			OnSkinChanged(skin);
+		}
+
+		protected virtual void OnSkinChanged(Skin skin)
+		{
+		}
 	}
 }
diff --git a/editor source/Desktop/Controls/RecordLookup.cs b/editor source/Desktop/Controls/RecordLookup.cs
index adcf8941f489fc9bbb6f31289c95a830d828c280..92a5aa0bea912aaa916303d43ea395513e42ba0a 100644
--- a/editor source/Desktop/Controls/RecordLookup.cs	
+++ b/editor source/Desktop/Controls/RecordLookup.cs	
@@ -1,16 +1,20 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Collections.Generic;
 using System.Windows.Forms;
 
 namespace Desktop
 {
-	public partial class RecordLookup : Form
+	public partial class RecordLookup : SkinnedForm
 	{
 		private static Dictionary<Type, IRecordProvider> _recordProviders;
 		private static Dictionary<Type, List<IRecord>> _recentRecords = new Dictionary<Type, List<IRecord>>();
 
+		public static bool IsOpen { get; set; }
+
 		public static IRecord Get(Type type, string key, bool allowCreate, object recordContext)
 		{
+			if (key == null) { return null; }
 			if (_recordProviders == null)
 			{
 				PrepRecordProviders();
@@ -92,14 +96,17 @@ namespace Desktop
 			RecordLookup form = new RecordLookup();
 			form.AllowCreate = allowCreate;
 			form.Text = provider.GetLookupCaption();
-			form.SetContext(type, text);
+			form.SetContext(type, text, filter);
 			form.Filter = filter;
+			IsOpen = true;
 			if (form.ShowDialog() == DialogResult.OK)
 			{
+				IsOpen = false;
 				return form.Record;
 			}
 			else
 			{
+				IsOpen = false;
 				return null;
 			}
 		}
@@ -211,7 +218,7 @@ namespace Desktop
 			return key;
 		}
 
-		public void SetContext(Type recordType, string startText)
+		public void SetContext(Type recordType, string startText, Func<IRecord, bool> filter)
 		{
 			_loading = true;
 			_recordType = recordType;
@@ -223,9 +230,11 @@ namespace Desktop
 			lstRecent.HeaderStyle = ColumnHeaderStyle.None;
 			lstRecent.Columns.Clear();
 			lstItems.Columns.Clear();
-			foreach (string col in _provider.GetColumns())
+			string[] cols = _provider.GetColumns();
+			for (int i = 0; i < cols.Length; i++)
 			{
-				int width = (lstItems.Columns.Count == 0 ? 150 : -2);
+				string col = cols[i];
+				int width = (i == 0 ? 150 : i < cols.Length - 1 ? 100 : -2);
 				lstItems.Columns.Add(col, width);
 				lstRecent.Columns.Add(col, width);
 			}
@@ -250,9 +259,12 @@ namespace Desktop
 				}
 				foreach (IRecord record in records)
 				{
-					ListViewItem item = _provider.FormatItem(record);
-					item.Tag = record;
-					lstRecent.Items.Insert(0, item);
+					if (filter == null || filter(record))
+					{
+						ListViewItem item = _provider.FormatItem(record);
+						item.Tag = record;
+						lstRecent.Items.Insert(0, item);
+					}
 				}
 			}
 			else
diff --git a/editor source/Desktop/Controls/RecordLookup.designer.cs b/editor source/Desktop/Controls/RecordLookup.designer.cs
index 5614b0ef8143d047409938a3dacf86eacb5a5b02..d6d4e45df2509273ca6d285b55818f7a08141003 100644
--- a/editor source/Desktop/Controls/RecordLookup.designer.cs	
+++ b/editor source/Desktop/Controls/RecordLookup.designer.cs	
@@ -28,21 +28,27 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.txtName = new System.Windows.Forms.TextBox();
-			this.cmdNew = new System.Windows.Forms.Button();
-			this.cmdAccept = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.lstItems = new System.Windows.Forms.ListView();
-			this.cmdDelete = new System.Windows.Forms.Button();
-			this.lblRecent = new System.Windows.Forms.Label();
-			this.lstRecent = new System.Windows.Forms.ListView();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.txtName = new Desktop.Skinning.SkinnedTextBox();
+			this.cmdNew = new Desktop.Skinning.SkinnedButton();
+			this.cmdAccept = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.lstItems = new Desktop.Skinning.SkinnedListView();
+			this.cmdDelete = new Desktop.Skinning.SkinnedButton();
+			this.lblRecent = new Desktop.Skinning.SkinnedLabel();
+			this.lstRecent = new Desktop.Skinning.SkinnedListView();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(9, 13);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label1.Location = new System.Drawing.Point(9, 34);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(38, 13);
 			this.label1.TabIndex = 0;
@@ -52,18 +58,25 @@
 			// 
 			this.txtName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.txtName.Location = new System.Drawing.Point(57, 10);
+			this.txtName.BackColor = System.Drawing.Color.White;
+			this.txtName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtName.ForeColor = System.Drawing.Color.Black;
+			this.txtName.Location = new System.Drawing.Point(57, 31);
 			this.txtName.Name = "txtName";
-			this.txtName.Size = new System.Drawing.Size(485, 20);
+			this.txtName.Size = new System.Drawing.Size(592, 20);
 			this.txtName.TabIndex = 1;
 			this.txtName.TextChanged += new System.EventHandler(this.txtName_TextChanged);
 			this.txtName.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txtName_KeyDown);
 			// 
 			// cmdNew
 			// 
-			this.cmdNew.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdNew.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdNew.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdNew.Enabled = false;
-			this.cmdNew.Location = new System.Drawing.Point(239, 326);
+			this.cmdNew.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdNew.Flat = true;
+			this.cmdNew.ForeColor = System.Drawing.Color.White;
+			this.cmdNew.Location = new System.Drawing.Point(355, 3);
 			this.cmdNew.Name = "cmdNew";
 			this.cmdNew.Size = new System.Drawing.Size(97, 23);
 			this.cmdNew.TabIndex = 3;
@@ -73,8 +86,12 @@
 			// 
 			// cmdAccept
 			// 
-			this.cmdAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdAccept.Location = new System.Drawing.Point(342, 326);
+			this.cmdAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdAccept.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdAccept.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdAccept.Flat = false;
+			this.cmdAccept.ForeColor = System.Drawing.Color.Blue;
+			this.cmdAccept.Location = new System.Drawing.Point(458, 3);
 			this.cmdAccept.Name = "cmdAccept";
 			this.cmdAccept.Size = new System.Drawing.Size(97, 23);
 			this.cmdAccept.TabIndex = 4;
@@ -84,9 +101,13 @@
 			// 
 			// cmdCancel
 			// 
-			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(445, 326);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(561, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(97, 23);
 			this.cmdCancel.TabIndex = 5;
@@ -102,10 +123,12 @@
 			this.lstItems.FullRowSelect = true;
 			this.lstItems.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
 			this.lstItems.HideSelection = false;
-			this.lstItems.Location = new System.Drawing.Point(12, 36);
+			this.lstItems.Location = new System.Drawing.Point(12, 57);
 			this.lstItems.MultiSelect = false;
 			this.lstItems.Name = "lstItems";
-			this.lstItems.Size = new System.Drawing.Size(530, 199);
+			this.lstItems.OwnerDraw = true;
+			this.lstItems.ShowItemToolTips = true;
+			this.lstItems.Size = new System.Drawing.Size(637, 199);
 			this.lstItems.TabIndex = 6;
 			this.lstItems.UseCompatibleStateImageBehavior = false;
 			this.lstItems.View = System.Windows.Forms.View.Tile;
@@ -113,8 +136,11 @@
 			// 
 			// cmdDelete
 			// 
-			this.cmdDelete.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdDelete.Location = new System.Drawing.Point(12, 326);
+			this.cmdDelete.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdDelete.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdDelete.Flat = true;
+			this.cmdDelete.ForeColor = System.Drawing.Color.White;
+			this.cmdDelete.Location = new System.Drawing.Point(3, 3);
 			this.cmdDelete.Name = "cmdDelete";
 			this.cmdDelete.Size = new System.Drawing.Size(97, 23);
 			this.cmdDelete.TabIndex = 7;
@@ -127,9 +153,13 @@
 			// 
 			this.lblRecent.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
 			this.lblRecent.AutoSize = true;
-			this.lblRecent.Location = new System.Drawing.Point(12, 238);
+			this.lblRecent.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.lblRecent.ForeColor = System.Drawing.Color.Blue;
+			this.lblRecent.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblRecent.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.lblRecent.Location = new System.Drawing.Point(12, 261);
 			this.lblRecent.Name = "lblRecent";
-			this.lblRecent.Size = new System.Drawing.Size(42, 13);
+			this.lblRecent.Size = new System.Drawing.Size(57, 21);
 			this.lblRecent.TabIndex = 8;
 			this.lblRecent.Text = "Recent";
 			// 
@@ -140,37 +170,50 @@
 			this.lstRecent.FullRowSelect = true;
 			this.lstRecent.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
 			this.lstRecent.HideSelection = false;
-			this.lstRecent.Location = new System.Drawing.Point(12, 254);
+			this.lstRecent.Location = new System.Drawing.Point(12, 285);
 			this.lstRecent.MultiSelect = false;
 			this.lstRecent.Name = "lstRecent";
-			this.lstRecent.Size = new System.Drawing.Size(530, 66);
+			this.lstRecent.OwnerDraw = true;
+			this.lstRecent.Size = new System.Drawing.Size(637, 71);
 			this.lstRecent.TabIndex = 9;
 			this.lstRecent.UseCompatibleStateImageBehavior = false;
 			this.lstRecent.View = System.Windows.Forms.View.Tile;
 			this.lstRecent.SelectedIndexChanged += new System.EventHandler(this.lstRecent_SelectedIndexChanged);
 			this.lstRecent.DoubleClick += new System.EventHandler(this.lstRecent_DoubleClick);
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdAccept);
+			this.skinnedPanel1.Controls.Add(this.cmdNew);
+			this.skinnedPanel1.Controls.Add(this.cmdDelete);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 362);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(661, 30);
+			this.skinnedPanel1.TabIndex = 10;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
 			// RecordLookup
 			// 
 			this.AcceptButton = this.cmdAccept;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(554, 361);
+			this.ClientSize = new System.Drawing.Size(661, 392);
 			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.lstRecent);
 			this.Controls.Add(this.lblRecent);
-			this.Controls.Add(this.cmdDelete);
 			this.Controls.Add(this.lstItems);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdAccept);
-			this.Controls.Add(this.cmdNew);
 			this.Controls.Add(this.txtName);
 			this.Controls.Add(this.label1);
 			this.Name = "RecordLookup";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Record Lookup";
 			this.Shown += new System.EventHandler(this.RecordLookup_Shown);
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -178,14 +221,15 @@
 
 		#endregion
 
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.TextBox txtName;
-		private System.Windows.Forms.Button cmdNew;
-		private System.Windows.Forms.Button cmdAccept;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.ListView lstItems;
-		private System.Windows.Forms.Button cmdDelete;
-		private System.Windows.Forms.Label lblRecent;
-		private System.Windows.Forms.ListView lstRecent;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedTextBox txtName;
+		private Desktop.Skinning.SkinnedButton cmdNew;
+		private Desktop.Skinning.SkinnedButton cmdAccept;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedListView lstItems;
+		private Desktop.Skinning.SkinnedButton cmdDelete;
+		private Desktop.Skinning.SkinnedLabel lblRecent;
+		private Desktop.Skinning.SkinnedListView lstRecent;
+		private Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/Desktop/Controls/WorkspaceControl.cs b/editor source/Desktop/Controls/WorkspaceControl.cs
index 6da272effe248f9ae3bbd7faf7bf80d3cb244c69..491cc7eadef3118d4d3cdb2a37ff9a88bb9b88d9 100644
--- a/editor source/Desktop/Controls/WorkspaceControl.cs	
+++ b/editor source/Desktop/Controls/WorkspaceControl.cs	
@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Drawing;
 using System.Reflection;
 using System.Windows.Forms;
 
@@ -21,52 +20,6 @@ namespace Desktop
 			InitializeComponent();
 		}
 
-		#region Tab drawing
-		private void tabActivities_DrawItem(object sender, DrawItemEventArgs e)
-		{
-			Graphics g = e.Graphics;
-			Brush textBrush;
-			Brush backBrush;
-
-			Rectangle rect = tabActivities.ClientRectangle;
-			//e.Graphics.FillRectangle(Brushes.LightSlateGray, rect);
-
-			// Get the item from the collection.
-			TabPage tabPage = tabActivities.TabPages[e.Index];
-
-			if (_spacers.Contains(tabPage))
-			{
-				return;
-			}
-
-			// Get the real bounds for the tab rectangle.
-			Rectangle tabBounds = tabActivities.GetTabRect(e.Index);
-
-			if (e.State == DrawItemState.Selected)
-			{
-				// Draw a different background color, and don't paint a focus rectangle.
-				textBrush = new SolidBrush(Color.White);
-				backBrush = new SolidBrush(Color.SlateBlue);
-				g.FillRectangle(backBrush, e.Bounds);
-			}
-			else
-			{
-				textBrush = new SolidBrush(e.ForeColor);
-				//e.DrawBackground();
-				g.FillRectangle(Brushes.White, e.Bounds);
-			}
-
-			// Use our own font.
-			Font tabFont = new Font("Arial", (float)11.0, FontStyle.Bold, GraphicsUnit.Pixel);
-
-			// Draw string. Center the text.
-			StringFormat stringFlags = new StringFormat();
-			stringFlags.Alignment = StringAlignment.Center;
-			stringFlags.LineAlignment = StringAlignment.Center;
-			g.DrawString(tabPage.Text, tabFont, textBrush, tabBounds, new StringFormat(stringFlags));
-		}
-		#endregion
-
 		internal void AddActivity(IActivity activity)
 		{
 			activity.Activated += Activity_Activated;
@@ -103,6 +56,7 @@ namespace Desktop
 		private void AddSpacer(WorkspacePane pane)
 		{
 			TabPage page = new TabPage();
+			page.Tag = "spacer";
 			switch (pane)
 			{
 				case WorkspacePane.Main:
@@ -163,7 +117,7 @@ namespace Desktop
 			TabPage page = tabActivities.TabPages[0];
 			Control ctl = page.Tag as Control;
 
-			DockTabPage(_tabs, tabActivities, splitContainer1.Panel1);
+			DockTabPage(_tabs, tabActivities, splitContainer1.Panel1, stripActivities);
 
 			//Update sidebar visibility
 			if (tabSidebarActivities.TabPages.Count == 0)
@@ -173,11 +127,11 @@ namespace Desktop
 			else
 			{
 				splitContainer1.Panel2Collapsed = false;
-				DockTabPage(_sideTabs, tabSidebarActivities, sidebar);
+				DockTabPage(_sideTabs, tabSidebarActivities, sidebar, stripSidebar);
 			}
 		}
 
-		private void DockTabPage(Dictionary<IActivity, TabPage> tabs, TabControl tabControl, Control parentControl)
+		private void DockTabPage(Dictionary<IActivity, TabPage> tabs, TabControl tabControl, Control parentControl, Control tabStrip)
 		{
 			TabPage page = tabControl.TabPages[0];
 			Control ctl = page.Tag as Control;
@@ -187,12 +141,14 @@ namespace Desktop
 				//Move the control out of the tabstrip
 				parentControl.Controls.Add(ctl);
 				tabControl.Visible = false;
+				tabStrip.Visible = false;
 			}
 			else
 			{
 				//put the control back into the tabstrip
 				page.Controls.Add(ctl);
 				tabControl.Visible = true;
+				tabStrip.Visible = true;
 			}
 		}
 
diff --git a/editor source/Desktop/Controls/WorkspaceControl.designer.cs b/editor source/Desktop/Controls/WorkspaceControl.designer.cs
index bb7fc78e56d2c2dc6e8ec365d12605aab569e573..20690df6ed2ffe2a79502e954ed272c2aa3a63dd 100644
--- a/editor source/Desktop/Controls/WorkspaceControl.designer.cs	
+++ b/editor source/Desktop/Controls/WorkspaceControl.designer.cs	
@@ -28,10 +28,12 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.tabActivities = new System.Windows.Forms.TabControl();
-			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+			this.splitContainer1 = new Desktop.Skinning.SkinnedSplitContainer();
+			this.stripActivities = new Desktop.Skinning.SkinnedTabStrip();
+			this.tabActivities = new Desktop.Skinning.SkinnedTabControl();
 			this.sidebar = new Desktop.CommonControls.DBPanel();
-			this.tabSidebarActivities = new System.Windows.Forms.TabControl();
+			this.stripSidebar = new Desktop.Skinning.SkinnedTabStrip();
+			this.tabSidebarActivities = new Desktop.Skinning.SkinnedTabControl();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
 			this.splitContainer1.Panel1.SuspendLayout();
 			this.splitContainer1.Panel2.SuspendLayout();
@@ -39,56 +41,101 @@
 			this.sidebar.SuspendLayout();
 			this.SuspendLayout();
 			// 
-			// tabActivities
-			// 
-			this.tabActivities.Alignment = System.Windows.Forms.TabAlignment.Left;
-			this.tabActivities.Dock = System.Windows.Forms.DockStyle.Fill;
-			this.tabActivities.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed;
-			this.tabActivities.ItemSize = new System.Drawing.Size(25, 100);
-			this.tabActivities.Location = new System.Drawing.Point(0, 0);
-			this.tabActivities.Multiline = true;
-			this.tabActivities.Name = "tabActivities";
-			this.tabActivities.SelectedIndex = 0;
-			this.tabActivities.Size = new System.Drawing.Size(617, 540);
-			this.tabActivities.SizeMode = System.Windows.Forms.TabSizeMode.Fixed;
-			this.tabActivities.TabIndex = 0;
-			this.tabActivities.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.tabActivities_DrawItem);
-			this.tabActivities.Selecting += new System.Windows.Forms.TabControlCancelEventHandler(this.OnSelectingTab);
-			// 
 			// splitContainer1
 			// 
-			this.splitContainer1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
 			this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.splitContainer1.Location = new System.Drawing.Point(0, 0);
 			this.splitContainer1.Name = "splitContainer1";
 			// 
 			// splitContainer1.Panel1
 			// 
+			this.splitContainer1.Panel1.Controls.Add(this.stripActivities);
 			this.splitContainer1.Panel1.Controls.Add(this.tabActivities);
 			// 
 			// splitContainer1.Panel2
 			// 
 			this.splitContainer1.Panel2.Controls.Add(this.sidebar);
 			this.splitContainer1.Size = new System.Drawing.Size(903, 544);
+			this.splitContainer1.SplitterColor = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
 			this.splitContainer1.SplitterDistance = 621;
 			this.splitContainer1.TabIndex = 1;
 			// 
+			// stripActivities
+			// 
+			this.stripActivities.Dock = System.Windows.Forms.DockStyle.Left;
+			this.stripActivities.Location = new System.Drawing.Point(0, 0);
+			this.stripActivities.Margin = new System.Windows.Forms.Padding(0);
+			this.stripActivities.Name = "stripActivities";
+			this.stripActivities.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
+			this.stripActivities.ShowCloseButton = false;
+			this.stripActivities.Size = new System.Drawing.Size(100, 544);
+			this.stripActivities.StartMargin = 10;
+			this.stripActivities.TabControl = this.tabActivities;
+			this.stripActivities.TabIndex = 1;
+			this.stripActivities.TabMargin = 1;
+			this.stripActivities.TabPadding = 20;
+			this.stripActivities.TabSize = 25;
+			this.stripActivities.TabType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.stripActivities.Text = "skinnedTabStrip1";
+			this.stripActivities.Vertical = true;
+			// 
+			// tabActivities
+			// 
+			this.tabActivities.Alignment = System.Windows.Forms.TabAlignment.Left;
+			this.tabActivities.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.tabActivities.ItemSize = new System.Drawing.Size(25, 100);
+			this.tabActivities.Location = new System.Drawing.Point(100, 0);
+			this.tabActivities.Margin = new System.Windows.Forms.Padding(0);
+			this.tabActivities.Multiline = true;
+			this.tabActivities.Name = "tabActivities";
+			this.tabActivities.SelectedIndex = 0;
+			this.tabActivities.Size = new System.Drawing.Size(521, 544);
+			this.tabActivities.SizeMode = System.Windows.Forms.TabSizeMode.Fixed;
+			this.tabActivities.TabIndex = 0;
+			this.tabActivities.Selecting += new System.Windows.Forms.TabControlCancelEventHandler(this.OnSelectingTab);
+			// 
 			// sidebar
 			// 
+			this.sidebar.Controls.Add(this.stripSidebar);
 			this.sidebar.Controls.Add(this.tabSidebarActivities);
 			this.sidebar.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.sidebar.Location = new System.Drawing.Point(0, 0);
 			this.sidebar.Name = "sidebar";
-			this.sidebar.Size = new System.Drawing.Size(274, 540);
+			this.sidebar.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.sidebar.Size = new System.Drawing.Size(278, 544);
 			this.sidebar.TabIndex = 0;
+			this.sidebar.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// stripSidebar
+			// 
+			this.stripSidebar.Dock = System.Windows.Forms.DockStyle.Top;
+			this.stripSidebar.Location = new System.Drawing.Point(0, 0);
+			this.stripSidebar.Margin = new System.Windows.Forms.Padding(0);
+			this.stripSidebar.Name = "stripSidebar";
+			this.stripSidebar.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.stripSidebar.ShowCloseButton = false;
+			this.stripSidebar.Size = new System.Drawing.Size(278, 23);
+			this.stripSidebar.StartMargin = 20;
+			this.stripSidebar.TabControl = this.tabSidebarActivities;
+			this.stripSidebar.TabIndex = 1;
+			this.stripSidebar.TabMargin = 5;
+			this.stripSidebar.TabPadding = 20;
+			this.stripSidebar.TabSize = -1;
+			this.stripSidebar.TabType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.stripSidebar.Vertical = false;
 			// 
 			// tabSidebarActivities
 			// 
-			this.tabSidebarActivities.Dock = System.Windows.Forms.DockStyle.Fill;
-			this.tabSidebarActivities.Location = new System.Drawing.Point(0, 0);
+			this.tabSidebarActivities.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.tabSidebarActivities.Location = new System.Drawing.Point(0, 23);
+			this.tabSidebarActivities.Margin = new System.Windows.Forms.Padding(0);
 			this.tabSidebarActivities.Name = "tabSidebarActivities";
 			this.tabSidebarActivities.SelectedIndex = 0;
-			this.tabSidebarActivities.Size = new System.Drawing.Size(274, 540);
+			this.tabSidebarActivities.Size = new System.Drawing.Size(278, 521);
 			this.tabSidebarActivities.TabIndex = 0;
 			this.tabSidebarActivities.Selecting += new System.Windows.Forms.TabControlCancelEventHandler(this.OnSelectingTab);
 			// 
@@ -110,9 +157,11 @@
 
 		#endregion
 
-		private System.Windows.Forms.TabControl tabActivities;
-		private System.Windows.Forms.SplitContainer splitContainer1;
+		private Desktop.Skinning.SkinnedTabControl tabActivities;
+		private Desktop.Skinning.SkinnedSplitContainer splitContainer1;
 		private CommonControls.DBPanel sidebar;
-		private System.Windows.Forms.TabControl tabSidebarActivities;
+		private Desktop.Skinning.SkinnedTabControl tabSidebarActivities;
+		private Skinning.SkinnedTabStrip stripActivities;
+		private Skinning.SkinnedTabStrip stripSidebar;
 	}
 }
diff --git a/editor source/Desktop/DataStructures/BindableObject.cs b/editor source/Desktop/DataStructures/BindableObject.cs
index c692b82e0bec945bce6f1a41f2c73445565d93f0..8b8088ca68fae6168c46e8a2cfa28d08cf2cb97e 100644
--- a/editor source/Desktop/DataStructures/BindableObject.cs	
+++ b/editor source/Desktop/DataStructures/BindableObject.cs	
@@ -82,9 +82,18 @@ namespace Desktop.DataStructures
 
 		private void RemoveHandlers<T>(T value, string propName)
 		{
-			//remove old CollectionChanged for collections
+			IList list = value as IList;
+			if (list != null)
+			{
+				//for lists, stop tracking changes to current items
+				foreach (object o in list)
+				{
+					UntrackListItem(propName, o);
+				}
+			}
 			if (_collectionHandlers.ContainsKey(propName))
 			{
+				//remove old CollectionChanged for collections
 				INotifyCollectionChanged col = value as INotifyCollectionChanged;
 				if (col != null)
 				{
@@ -109,10 +118,19 @@ namespace Desktop.DataStructures
 
 		private void AttachHandlers<T>(T value, string propName)
 		{
-			//attach CollectionChanged for collections
+			IList list = value as IList;
+			if (list != null)
+			{
+				//for lists, track changes to current items
+				foreach (object o in list)
+				{
+					TrackListItem(propName, o);
+				}
+			}
 			INotifyCollectionChanged collection = value as INotifyCollectionChanged;
 			if (collection != null)
 			{
+				//attach CollectionChanged for collections
 				NotifyCollectionChangedEventHandler handler = new NotifyCollectionChangedEventHandler((sender, e) =>
 				{
 					Collection_CollectionChanged(propName, sender, e);
@@ -163,16 +181,19 @@ namespace Desktop.DataStructures
 					}
 					break;
 				case NotifyCollectionChangedAction.Reset:
-					List<BindableObject> toRemove = new List<BindableObject>();
-					foreach (KeyValuePair<BindableObject, PropertyChangedEventHandler> kvp in _listItemHandlers[propName])
-					{
-						toRemove.Add(kvp.Key);
-					}
-					foreach (BindableObject key in toRemove)
+					if (_listItemHandlers.ContainsPrimaryKey(propName))
 					{
-						UntrackListItem(propName, key);
+						List<BindableObject> toRemove = new List<BindableObject>();
+						foreach (KeyValuePair<BindableObject, PropertyChangedEventHandler> kvp in _listItemHandlers[propName])
+						{
+							toRemove.Add(kvp.Key);
+						}
+						foreach (BindableObject key in toRemove)
+						{
+							UntrackListItem(propName, key);
+						}
+						_listItemHandlers.Remove(propName);
 					}
-					_listItemHandlers.Remove(propName);
 					break;
 			}
 			NotifyPropertyChanged(propName);
diff --git a/editor source/Desktop/DataStructures/Category.cs b/editor source/Desktop/DataStructures/Category.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2e9d716ae5a26d706303c2e1ff96dbcecbe9a9d7
--- /dev/null
+++ b/editor source/Desktop/DataStructures/Category.cs	
@@ -0,0 +1,29 @@
+namespace Desktop
+{
+	public class Category : IRecord
+	{
+		public Category(string key, string value)
+		{
+			Key = key;
+			Name = value;
+		}
+
+		public string Group
+		{
+			get { return ""; }
+		}
+
+		public string Key { get; set; }
+		public string Name { get; set; }
+
+		public int CompareTo(IRecord other)
+		{
+			return Key.CompareTo(other.Key);
+		}
+
+		public string ToLookupString()
+		{
+			return $"{Name} [{Key}]";
+		}
+	}
+}
diff --git a/editor source/Desktop/DataStructures/GroupedList.cs b/editor source/Desktop/DataStructures/GroupedList.cs
new file mode 100644
index 0000000000000000000000000000000000000000..423a214754a9d229e991de53c2bb3fca2f13e501
--- /dev/null
+++ b/editor source/Desktop/DataStructures/GroupedList.cs	
@@ -0,0 +1,712 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace Desktop.CommonControls
+{
+	/// <summary>
+	/// List of bindable lists that have group headers. Assumes that an item can only appear in the list once
+	/// </summary>
+	public class GroupedList<T> : IGroupedList where T : class, IGroupedItem, INotifyPropertyChanged
+	{
+		/// <summary>
+		/// List of all top-level groups
+		/// </summary>
+		private List<Group> _groups { get; set; } = new List<Group>();
+		/// <summary>
+		/// Map between group keys and group objects
+		/// </summary>
+		private Dictionary<string, Group> _groupMap = new Dictionary<string, Group>();
+		/// <summary>
+		/// Mapping between items and what group they belong to
+		/// </summary>
+		private Dictionary<T, Group> _groupIndex = new Dictionary<T, Group>();
+		/// <summary>
+		/// Mapping between items and their index within a group
+		/// </summary>
+		private Dictionary<T, int> _index = new Dictionary<T, int>();
+		/// <summary>
+		/// Mapping between items and their PropertyChanged handlers
+		/// </summary>
+		private Dictionary<T, PropertyChangedEventHandler> _propertyHandlers = new Dictionary<T, PropertyChangedEventHandler>();
+
+		public event EventHandler<GroupedListMovingEventArgs> BeforeMovingItem;
+		public event EventHandler<GroupedListMovingEventArgs> AfterMovingItem;
+		public event EventHandler<GroupedListChangedEventArgs> ListChanged;
+
+		public Func<T, T, int> ItemComparer;
+		public Func<string, string, int> GroupComparer;
+
+		private bool _sorted;
+		public bool Sorted
+		{
+			get { return _sorted; }
+			set
+			{
+				if (_sorted != value)
+				{
+					_sorted = value;
+					if (_sorted)
+					{
+						SortItems();
+					}
+				}
+			}
+		}
+
+		public void AddGroup(string key)
+		{
+			AddGroupPrivate(key);
+		}
+		private Group AddGroupPrivate(string key)
+		{
+			string[] chain = key.Split(new string[] { ">" }, StringSplitOptions.RemoveEmptyEntries);
+			Group parentGroup = null;
+			string fullPath = null;
+			for (int i = 0; i < chain.Length; i++)
+			{
+				string groupKey = chain[i];
+				if (fullPath == null)
+				{
+					fullPath = groupKey;
+				}
+				else
+				{
+					fullPath += ">" + groupKey;
+				}
+				Group group;
+				if (!_groupMap.TryGetValue(fullPath, out group))
+				{
+					GroupedListGrouper grouper = new GroupedListGrouper(fullPath, groupKey);
+					group = new Group(grouper, parentGroup);
+					_groupMap[fullPath] = group;
+					if (parentGroup == null)
+					{
+						_groups.Add(group);
+						_groups.Sort(SortGroups);
+						for (int j = 0; j < _groups.Count; j++)
+						{
+							_groups[j].Index = j;
+						}
+					}
+					else
+					{
+						parentGroup.SubGroups.Add(group);
+						parentGroup.SubGroups.Sort(SortGroups);
+						for(int j = 0; j < parentGroup.SubGroups.Count; j++)
+						{
+							parentGroup.SubGroups[j].Index = j;
+						}
+					}
+				}
+				parentGroup = group;
+			}
+			return parentGroup;
+		}
+
+		private int SortGroups(Group g1, Group g2)
+		{
+			if (GroupComparer != null)
+			{
+				return GroupComparer(g1.Key, g2.Key);
+			}
+			return g1.Key.CompareTo(g2.Key);
+		}
+
+		/// <summary>
+		/// Current count of expanded groups and items. If an item is behind a collapsed group, it is not counted here
+		/// </summary>
+		public int Count
+		{
+			get
+			{
+				int count = 0;
+				for (int i = 0; i < _groups.Count; i++)
+				{
+					count++;
+					Group g = _groups[i];
+					count += g.GetExpandedCount();
+				}
+				return count;
+			}
+		}
+
+		/// <summary>
+		/// Gets the number of first-level items contained under a group path, regardless of collapsed state
+		/// </summary>
+		/// <param name="path"></param>
+		/// <returns></returns>
+		public int GetGroupCount(string path)
+		{
+			Group group;
+			if (_groupMap.TryGetValue(path, out group))
+			{
+				return group.Items.Count + group.SubGroups.Count;
+			}
+			return -1;
+		}
+
+		/// <summary>
+		/// Gets the item or group occupying a virtual index
+		/// </summary>
+		/// <param name="index"></param>
+		/// <returns></returns>
+		public GroupedListItem GetItem(int index)
+		{
+			object item;
+			for (int i = 0; i < _groups.Count; i++)
+			{
+				Group g = _groups[i];
+				Group parent = null;
+				item = g.GetItem(ref index, ref parent);
+				if (item != null)
+				{
+					bool lastInGroup = false;
+					if (item is T)
+					{
+						lastInGroup = _index[(T)item] == parent.Items.Count - 1;
+					}
+					return new GroupedListItem(item, parent?.Grouper, lastInGroup);
+				}
+			}
+			return null;
+		}
+
+		public void SortItems()
+		{
+			if (ItemComparer != null)
+			{
+				foreach (Group group in _groups)
+				{
+					SortGroup(group);
+				}
+			}
+		}
+
+		private void SortGroup(Group group)
+		{
+			foreach (Group subgroup in group.SubGroups)
+			{
+				SortGroup(subgroup);
+			}
+			group.Items.Sort(SortItemsMethod);
+			IndexGroup(group);
+		}
+
+		private int SortItemsMethod(T item1, T item2)
+		{
+			return ItemComparer(item1, item2);
+		}
+
+		public void AddItem(T item)
+		{
+			string groupKey = item.GetGroupKey();
+			Group group;
+			if (!_groupMap.TryGetValue(groupKey, out group))
+			{
+				group = AddGroupPrivate(groupKey);
+			}
+			bool added = false;
+			if (_sorted && ItemComparer != null)
+			{
+				for (int i = 0; i < group.Items.Count; i++)
+				{
+					int compare = ItemComparer(group.Items[i], item);
+					if (compare > 0)
+					{
+						group.Items.Insert(i, item);
+						added = true;
+						break;
+					}
+				}
+			}
+			if (!added)
+			{
+				group.Items.Add(item);
+			}
+
+			IndexGroup(group);
+			_groupIndex[item] = group;
+			int index = GetIndex(item);
+			PropertyChangedEventHandler handler = new PropertyChangedEventHandler((sender, e) =>
+			{
+				OnItemModified(item);
+			});
+			item.PropertyChanged += handler;
+			_propertyHandlers[item] = handler;
+			ListChanged?.Invoke(this, new GroupedListChangedEventArgs(GroupedListChangedAction.Add, item, index));
+		}
+
+		private void OnItemModified(T item)
+		{
+			int index = GetIndex(item);
+			string key = item.GetGroupKey();
+			Group indexedGroup = _groupIndex[item];
+			if (indexedGroup.Path != key)
+			{
+				//group changed; need to remove and add back, which will also do a re-sort
+				GroupedListMovingEventArgs modifyArgs = new GroupedListMovingEventArgs(item, index);
+				BeforeMovingItem?.Invoke(this, modifyArgs);
+				RemoveItem(item);
+				AddItem(item);
+				AfterMovingItem?.Invoke(this, modifyArgs);
+			}
+			else
+			{
+				//auto-resort disabled for now since it makes selecting a node that causes a resort that moves that node to behave funky
+
+				////see if we need to re-sort this
+				//if (_sorted && ItemComparer != null)
+				//{
+				//	bool sorted = false;
+				//	int indexedPos = _index[item];
+				//	int newIndex = indexedPos;
+				//	for (int i = 0; i < indexedGroup.Items.Count; i++)
+				//	{
+				//		if (i == indexedPos)
+				//		{
+				//			continue;
+				//		}
+				//		int compare = ItemComparer(indexedGroup.Items[i], item);
+				//		if (compare > 0)
+				//		{
+				//			sorted = true;
+				//			if (indexedPos != i - 1)
+				//			{
+				//				newIndex = i;
+				//			}
+				//			break;
+				//		}
+				//	}
+				//	if (!sorted && indexedPos < indexedGroup.Items.Count - 1)
+				//	{
+				//		//move to the end
+				//		newIndex = indexedGroup.Items.Count;
+				//	}
+				//	if (newIndex != indexedPos)
+				//	{
+				//		GroupedListMovingEventArgs modifyArgs = new GroupedListMovingEventArgs(item, index);
+				//		BeforeMovingItem?.Invoke(this, modifyArgs);
+				//		if (newIndex > indexedPos)
+				//		{
+				//			newIndex--;
+				//		}
+				//		indexedGroup.Items.RemoveAt(indexedPos);
+				//		indexedGroup.Items.Insert(newIndex, item);
+				//		IndexGroup(indexedGroup);
+				//		AfterMovingItem?.Invoke(this, modifyArgs);
+				//	}
+				//}
+
+				ListChanged?.Invoke(this, new GroupedListChangedEventArgs(GroupedListChangedAction.Modify, item, index));
+			}
+		}
+
+		public void RemoveItem(T item)
+		{
+			string groupKey = item.GetGroupKey();
+			Group group;
+			if (_groupIndex.TryGetValue(item, out group))
+			{
+				int index = _index[item];
+				int absIndex = GetIndex(item);
+				group.Items.RemoveAt(index);
+				IndexGroup(group);
+				ListChanged?.Invoke(this, new GroupedListChangedEventArgs(GroupedListChangedAction.Remove, item, absIndex));
+				PropertyChangedEventHandler handler = _propertyHandlers[item];
+				item.PropertyChanged -= handler;
+				_propertyHandlers.Remove(item);
+				_groupIndex.Remove(item);
+			}
+		}
+
+		/// <summary>
+		/// Gets the virtual index of an item
+		/// </summary>
+		/// <param name="item"></param>
+		/// <returns></returns>
+		public int GetIndex(object item)
+		{
+			if (item == null) { return -1; }
+			int index = 0;
+			for (int i = 0; i < _groups.Count; i++)
+			{
+				if (GetIndex(_groups[i], item, ref index))
+				{
+					return index;
+				}
+			}
+			return -1;
+		}
+
+		private bool GetIndex(Group group, object item, ref int index)
+		{
+			if (item is GroupedListGrouper)
+			{
+				if (group.Grouper.Equals(item))
+				{
+					return true;
+				}
+				index++; //count the group
+				if (group.Expanded)
+				{
+					foreach (Group subgroup in group.SubGroups)
+					{
+						if (GetIndex(subgroup, item, ref index))
+						{
+							return true;
+						}
+					}
+				}
+			}
+			else
+			{
+				Group indexedGroup = _groupIndex[(T)item];
+				index++; //count the group
+				if (group.Expanded)
+				{
+					foreach (Group subgroup in group.SubGroups)
+					{
+						if (GetIndex(subgroup, item, ref index))
+						{
+							return true;
+						}
+					}
+					if (indexedGroup == group)
+					{
+						//item is in this group
+						int groupIndex = _index[(T)item];
+						index += groupIndex;
+						return true;
+					}
+					else
+					{
+						//item is not in this group, so skip iterating it
+						index += group.Items.Count;
+					}
+				}
+				else if (indexedGroup == group)
+				{
+					//item is in this group, but it's collapsed
+					index = -1;
+					return true;
+				}
+			}
+			return false;
+		}
+
+		private void IndexGroup(Group group)
+		{
+			for (int i = 0; i < group.SubGroups.Count; i++)
+			{
+				Group subgroup = group.SubGroups[i];
+				subgroup.Index = i;
+				IndexGroup(subgroup);
+			}
+			for (int i = 0; i < group.Items.Count; i++)
+			{
+				_index[group.Items[i]] = i;
+			}
+			Group parent = group.Parent;
+			Group root = group;
+			int depth = 0;
+			while (parent != null)
+			{
+				depth++;
+				root = parent;
+				parent = parent.Parent;
+			}
+			group.Depth = depth;
+			parent = group.Parent;
+			while (parent != null && depth > 1)
+			{
+				parent.Depth = --depth;
+				parent = parent.Parent;
+			}
+			group.RootKey = root.Key;
+		}
+
+		public int GetDepth(object item)
+		{
+			Group group;
+			if (_groupIndex.TryGetValue((T)item, out group))
+			{
+				return group.Depth;
+			}
+			return 0;
+		}
+
+		public void ToggleGroup(string key, bool expanded)
+		{
+			Group group;
+			if (_groupMap.TryGetValue(key, out group))
+			{
+				if (group.Expanded != expanded)
+				{
+					group.Expanded = expanded;
+					ListChanged?.Invoke(this, new GroupedListChangedEventArgs(GroupedListChangedAction.GroupToggled, group.Grouper, -1));
+				}
+			}
+		}
+
+		public void ExpandAll()
+		{
+			foreach (Group group in _groupMap.Values)
+			{
+				ToggleGroup(group.Path, true);
+			}
+		}
+
+		/// <summary>
+		/// Expands all the groups leading up to an item
+		/// </summary>
+		/// <param name="item"></param>
+		public void ExpandTo(object item)
+		{
+			T model = item as T;
+			if (model == null)
+			{
+				return;
+			}
+
+			Group group;
+			if (_groupIndex.TryGetValue(model, out group))
+			{
+				while (group != null)
+				{
+					if (!group.Expanded)
+					{
+						ToggleGroup(group.Path, true);
+					}
+					group = group.Parent;
+				}
+			}
+		}
+
+		private class Group
+		{
+			public int Depth { get { return Grouper.Depth; } set { Grouper.Depth = value; } }
+			public int Index { get { return Grouper.Index; } set { Grouper.Index = value; } }
+			public string RootKey { get { return Grouper.RootKey; } set { Grouper.RootKey = value; } }
+			public Group Parent;
+			public GroupedListGrouper Grouper;
+			public string Path { get { return Grouper.Path; } }
+			public string Key { get { return Grouper.Key; } }
+			public List<Group> SubGroups = new List<Group>();
+			public List<T> Items = new List<T>();
+			public bool Expanded
+			{
+				get
+				{
+					return Grouper.Expanded;
+				}
+				set
+				{
+					Grouper.Expanded = value;
+				}
+			}
+
+			public Group(GroupedListGrouper grouper, Group parent)
+			{
+				Grouper = grouper;
+				Parent = parent;
+			}
+
+			/// <summary>
+			/// Gets the number of items expanded under this group (not including this group itself)
+			/// </summary>
+			/// <returns></returns>
+			public int GetExpandedCount()
+			{
+				int count = 0;
+				if (Expanded)
+				{
+					count += Items.Count;
+					for (int i = 0; i < SubGroups.Count; i++)
+					{
+						count++;
+						count += SubGroups[i].GetExpandedCount();
+					}
+				}
+				return count;
+			}
+
+			/// <summary>
+			/// Gets the item at a virtual index, which can change depending on the collapsed states
+			/// </summary>
+			/// <param name="index">Index to lookup. Output is input - number of virtual items were examined</param>
+			/// <returns>Item at the index if found</returns>
+			public object GetItem(ref int index, ref Group parentGroup)
+			{
+				if (index == 0)
+				{
+					return Grouper;
+				}
+				index--;
+				if (Expanded)
+				{
+					foreach (Group subgroup in SubGroups)
+					{
+						object item = subgroup.GetItem(ref index, ref parentGroup);
+						if (item != null)
+						{
+							parentGroup = subgroup;
+							return item;
+						}
+					}
+					if (index < Items.Count)
+					{
+						parentGroup = this;
+						return Items[index];
+					}
+					index -= Items.Count;
+				}
+				parentGroup = null;
+				return null;
+			}
+		}
+	}
+
+	/// <summary>
+	/// Public accessor to a group
+	/// </summary>
+	public class GroupedListGrouper
+	{
+		public int Depth { get; internal set; }
+
+		/// <summary>
+		/// Full group chain to this one
+		/// </summary>
+		public string Path { get; private set; }
+		/// <summary>
+		/// Group's unique key
+		/// </summary>
+		public string Key { get; private set; }
+		/// <summary>
+		/// Whether this group is expanded
+		/// </summary>
+		public bool Expanded { get; internal set; }
+
+		/// <summary>
+		/// Groups index within its parent
+		/// </summary>
+		public int Index;
+
+		/// <summary>
+		/// Key for the root node
+		/// </summary>
+		public string RootKey;
+
+		public GroupedListGrouper(string path, string key)
+		{
+			Path = path;
+			Key = key;
+			Expanded = false;
+		}
+	}
+
+	public interface IGroupedList
+	{
+		int Count { get; }
+		GroupedListItem GetItem(int index);
+		/// <summary>
+		/// Gets the virtual index of item based on the groups' collapsed states
+		/// </summary>
+		/// <param name="item"></param>
+		/// <returns></returns>
+		int GetIndex(object item);
+		/// <summary>
+		/// Gets an item's depth
+		/// </summary>
+		/// <param name="item"></param>
+		/// <returns></returns>
+		int GetDepth(object item);
+		/// <summary>
+		/// Gets the number of items under a group
+		/// </summary>
+		/// <param name="key"></param>
+		/// <returns></returns>
+		int GetGroupCount(string key);
+		void ToggleGroup(string key, bool expanded);
+		/// <summary>
+		/// Forces a item's group to expand if it isn't already
+		/// </summary>
+		/// <param name="item">Item to expand to</param>
+		void ExpandTo(object item);
+		void ExpandAll();
+		event EventHandler<GroupedListChangedEventArgs> ListChanged;
+		event EventHandler<GroupedListMovingEventArgs> BeforeMovingItem;
+		event EventHandler<GroupedListMovingEventArgs> AfterMovingItem;
+	}
+
+	public interface IGroupedItem
+	{
+		string GetGroupKey();
+	}
+
+	public class GroupedListChangedEventArgs : EventArgs
+	{
+		public GroupedListChangedAction Action { get; private set; }
+		public int Index { get; private set; }
+		public object Item { get; private set; }
+
+
+		public GroupedListChangedEventArgs(GroupedListChangedAction action) : this(action, null, -1) { }
+		public GroupedListChangedEventArgs(GroupedListChangedAction action, object item, int index)
+		{
+			Action = action;
+			Item = item;
+			Index = index;
+		}
+	}
+
+	public class GroupedListMovingEventArgs : EventArgs
+	{
+		public object Item;
+		public int OriginalIndex;
+
+		public GroupedListMovingEventArgs(object item, int index)
+		{
+			Item = item;
+			OriginalIndex = index;
+		}
+	}
+
+	public enum GroupedListChangedAction
+	{
+		/// <summary>
+		/// One item was added
+		/// </summary>
+		Add,
+		/// <summary>
+		/// One item was removed
+		/// </summary>
+		Remove,
+		/// <summary>
+		/// All items were removed
+		/// </summary>
+		Clear,
+		/// <summary>
+		/// One item was modified
+		/// </summary>
+		Modify,
+		/// <summary>
+		/// A group was expanded or collapsed
+		/// </summary>
+		GroupToggled,
+	}
+
+	public class GroupedListItem
+	{
+		public object Data;
+		public GroupedListGrouper Group;
+		public bool LastInGroup;
+
+		public GroupedListItem(object data, GroupedListGrouper group, bool lastInGroup)
+		{
+			Data = data;
+			Group = group;
+			LastInGroup = lastInGroup;
+		}
+	}
+}
diff --git a/editor source/Desktop/DataStructures/ObservableSet.cs b/editor source/Desktop/DataStructures/ObservableSet.cs
new file mode 100644
index 0000000000000000000000000000000000000000..38a62c3f01fc567b11078204a71771e650e5642d
--- /dev/null
+++ b/editor source/Desktop/DataStructures/ObservableSet.cs	
@@ -0,0 +1,150 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+
+namespace Desktop
+{
+	public class ObservableSet<T> : ISet<T>, INotifyCollectionChanged, ICloneable
+	{
+		private HashSet<T> _set = new HashSet<T>();
+
+		public int Count
+		{
+			get
+			{
+				return _set.Count;
+			}
+		}
+
+		public bool IsReadOnly
+		{
+			get
+			{
+				return false;
+			}
+		}
+
+		public event NotifyCollectionChangedEventHandler CollectionChanged;
+
+		public bool Add(T item)
+		{
+			if (!_set.Contains(item))
+			{
+				_set.Add(item);
+				CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
+				return true;
+			}
+			return false;
+		}
+
+		public void Clear()
+		{
+			_set.Clear();
+			CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+		}
+
+		public object Clone()
+		{
+			ObservableSet<T> copy = new ObservableSet<T>();
+			foreach (T value in _set)
+			{
+				ICloneable cloneableItem = value as ICloneable;
+				if (cloneableItem != null)
+				{
+					copy.Add((T)cloneableItem.Clone());
+				}
+				else
+				{
+					copy.Add(value);
+				}
+			}
+			return copy;
+		}
+
+		public bool Contains(T item)
+		{
+			return _set.Contains(item);
+		}
+
+		public void CopyTo(T[] array, int arrayIndex)
+		{
+			_set.CopyTo(array, arrayIndex);
+		}
+
+		public void ExceptWith(IEnumerable<T> other)
+		{
+			_set.ExceptWith(other);
+		}
+
+		public IEnumerator<T> GetEnumerator()
+		{
+			return _set.GetEnumerator();
+		}
+
+		public void IntersectWith(IEnumerable<T> other)
+		{
+			_set.IntersectWith(other);
+		}
+
+		public bool IsProperSubsetOf(IEnumerable<T> other)
+		{
+			return _set.IsProperSubsetOf(other);
+		}
+
+		public bool IsProperSupersetOf(IEnumerable<T> other)
+		{
+			return _set.IsProperSupersetOf(other);
+		}
+
+		public bool IsSubsetOf(IEnumerable<T> other)
+		{
+			return _set.IsSubsetOf(other);
+		}
+
+		public bool IsSupersetOf(IEnumerable<T> other)
+		{
+			return _set.IsSupersetOf(other);
+		}
+
+		public bool Overlaps(IEnumerable<T> other)
+		{
+			return _set.Overlaps(other);
+		}
+
+		public bool Remove(T item)
+		{
+			if (_set.Remove(item))
+			{
+				CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
+				return true;
+			}
+			return false;
+		}
+
+		public bool SetEquals(IEnumerable<T> other)
+		{
+			return _set.SetEquals(other);
+		}
+
+		public void SymmetricExceptWith(IEnumerable<T> other)
+		{
+			_set.SymmetricExceptWith(other);
+		}
+
+		public void UnionWith(IEnumerable<T> other)
+		{
+			_set.UnionWith(other);
+		}
+
+		void ICollection<T>.Add(T item)
+		{
+			Add(item);
+		}
+
+		IEnumerator IEnumerable.GetEnumerator()
+		{
+			return GetEnumerator();
+		}
+	}
+}
diff --git a/editor source/Desktop/DataStructures/PropertyMacro.cs b/editor source/Desktop/DataStructures/PropertyMacro.cs
index 1afce243db3b38102ec679f42e91a0756d7db8ea..44e4814b5be38dc5a3bbb990c72d55691d9bed2b 100644
--- a/editor source/Desktop/DataStructures/PropertyMacro.cs	
+++ b/editor source/Desktop/DataStructures/PropertyMacro.cs	
@@ -13,10 +13,7 @@ namespace Desktop
 
 		public List<PropertyMacro> Properties = new List<PropertyMacro>();
 
-		public string Group
-		{
-			get { return ""; }
-		}
+		public string Group { get; set; }
 
 		public string Key
 		{
@@ -72,7 +69,7 @@ namespace Desktop
 			{
 				applications.Add(prop.Serialize());
 			}
-			return $"{Name}$#{string.Join("#$", applications)}";
+			return $"{Name}$#{string.Join("#$", applications)}$#{Group}";
 		}
 
 		public static Macro Deserialize(string data)
@@ -88,6 +85,10 @@ namespace Desktop
 					PropertyMacro property = PropertyMacro.Deserialize(piece);
 					macro.Properties.Add(property);
 				}
+				if (pieces.Length > 2)
+				{
+					macro.Group = pieces[2];
+				}
 				return macro;
 			}
 			return null;
@@ -139,6 +140,10 @@ namespace Desktop
 			string pattern = @"(\$\w+)";
 			foreach (string value in Values)
 			{
+				if (value == null)
+				{
+					continue;
+				}
 				if (value.Contains("$"))
 				{
 					MatchCollection matches = Regex.Matches(value, pattern);
diff --git a/editor source/Desktop/Desktop.csproj b/editor source/Desktop/Desktop.csproj
index a6278600a2009a140545316af3c5496c837a9a52..6d774d606583f8824f2b0fdbdd699d72e747fde7 100644
--- a/editor source/Desktop/Desktop.csproj	
+++ b/editor source/Desktop/Desktop.csproj	
@@ -30,10 +30,15 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\SPNATI Character Editor\Newtonsoft.Json.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="System.Drawing" />
     <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Windows.Forms.DataVisualization" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
@@ -42,7 +47,29 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="ALV\AccordionColumn.cs" />
+    <Compile Include="ALV\FormatRowEventArgs.cs" />
     <Compile Include="BasicRecord.cs" />
+    <Compile Include="CommonControls\AccordionListView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="CommonControls\AccordionListView.Designer.cs">
+      <DependentUpon>AccordionListView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="CommonControls\ColorField.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="CommonControls\DataGridView\RecordCell.cs" />
+    <Compile Include="CommonControls\DataGridView\RecordColumn.cs" />
+    <Compile Include="CommonControls\DataGridView\RecordEditingControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="CommonControls\DataSlicerControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="CommonControls\DataSlicerControl.Designer.cs">
+      <DependentUpon>DataSlicerControl.cs</DependentUpon>
+    </Compile>
     <Compile Include="CommonControls\DBTreeView.cs">
       <SubType>Component</SubType>
     </Compile>
@@ -67,6 +94,12 @@
     <Compile Include="CommonControls\PropertyControls\ColorControl.Designer.cs">
       <DependentUpon>ColorControl.cs</DependentUpon>
     </Compile>
+    <Compile Include="CommonControls\PropertyControls\ColorSetControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="CommonControls\PropertyControls\ColorSetControl.Designer.cs">
+      <DependentUpon>ColorSetControl.cs</DependentUpon>
+    </Compile>
     <Compile Include="CommonControls\PropertyControls\ComboBoxControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -151,24 +184,12 @@
     <Compile Include="CommonControls\EnumSelect.designer.cs">
       <DependentUpon>EnumSelect.cs</DependentUpon>
     </Compile>
-    <Compile Include="CommonControls\ListSelect.cs">
-      <SubType>Form</SubType>
-    </Compile>
-    <Compile Include="CommonControls\ListSelect.designer.cs">
-      <DependentUpon>ListSelect.cs</DependentUpon>
-    </Compile>
     <Compile Include="Controls\RecordLookup.cs">
       <SubType>Form</SubType>
     </Compile>
     <Compile Include="Controls\RecordLookup.designer.cs">
       <DependentUpon>RecordLookup.cs</DependentUpon>
     </Compile>
-    <Compile Include="CommonControls\Slider.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="CommonControls\Slider.designer.cs">
-      <DependentUpon>Slider.cs</DependentUpon>
-    </Compile>
     <Compile Include="Controls\WorkspaceControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -176,10 +197,13 @@
       <DependentUpon>WorkspaceControl.cs</DependentUpon>
     </Compile>
     <Compile Include="DataStructures\BindableObject.cs" />
+    <Compile Include="DataStructures\Category.cs" />
     <Compile Include="DataStructures\Clipboards.cs" />
     <Compile Include="DataStructures\DualKeyDictionary.cs" />
     <Compile Include="DataStructures\DuplicateKeyComparer.cs" />
+    <Compile Include="DataStructures\GroupedList.cs" />
     <Compile Include="DataStructures\ObservableDictionary.cs" />
+    <Compile Include="DataStructures\ObservableSet.cs" />
     <Compile Include="DataStructures\PropertyMacro.cs" />
     <Compile Include="DataStructures\PropertyRecord.cs" />
     <Compile Include="Extensions\NativeMethods.cs" />
@@ -205,6 +229,7 @@
     <Compile Include="Interfaces\IMacroEditor.cs" />
     <Compile Include="Interfaces\IRecord.cs" />
     <Compile Include="Interfaces\IWorkspace.cs" />
+    <Compile Include="Messaging\CoreDesktopMessages.cs" />
     <Compile Include="Messaging\Mailbox.cs" />
     <Compile Include="Messaging\PostOffice.cs" />
     <Compile Include="Messaging\Subscription.cs" />
@@ -217,9 +242,44 @@
       <DependentUpon>Resources.resx</DependentUpon>
     </Compile>
     <Compile Include="Providers\BasicProvider.cs" />
+    <Compile Include="Providers\CategoryProvider.cs" />
     <Compile Include="Providers\IRecordProvider.cs" />
     <Compile Include="Providers\MacroProvider.cs" />
     <Compile Include="Providers\PropertyProvider.cs" />
+    <Compile Include="Providers\SkinProvider.cs" />
+    <Compile Include="Providers\SlicerProvider.cs" />
+    <Compile Include="Reporting\BaseSlicer.cs" />
+    <Compile Include="Reporting\BooleanSlicer.cs" />
+    <Compile Include="Reporting\ComboSlicer.cs" />
+    <Compile Include="Reporting\Controls\BooleanSlicerControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Reporting\Controls\BooleanSlicerControl.Designer.cs">
+      <DependentUpon>BooleanSlicerControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Reporting\Controls\ComboSlicerControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Reporting\Controls\ComboSlicerControl.Designer.cs">
+      <DependentUpon>ComboSlicerControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Reporting\Controls\RecordSlicerControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Reporting\Controls\RecordSlicerControl.Designer.cs">
+      <DependentUpon>RecordSlicerControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Reporting\Controls\SlicerGroupEntry.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Reporting\Controls\SlicerGroupEntry.Designer.cs">
+      <DependentUpon>SlicerGroupEntry.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Reporting\DataSlicer.cs" />
+    <Compile Include="Reporting\DataSlicerControlMap.cs" />
+    <Compile Include="Reporting\RecordSlicer.cs" />
+    <Compile Include="Reporting\SlicerGroup.cs" />
+    <Compile Include="Serialization\Json.cs" />
     <Compile Include="Serialization\PropertyTypeInfo.cs" />
     <Compile Include="Shell.cs">
       <SubType>Form</SubType>
@@ -227,21 +287,115 @@
     <Compile Include="Shell.designer.cs">
       <DependentUpon>Shell.cs</DependentUpon>
     </Compile>
+    <Compile Include="Skinning\Animator.cs" />
+    <Compile Include="Skinning\ColorSet.cs" />
+    <Compile Include="Skinning\DataGridView\SkinnedDataGridViewButtonCell.cs" />
+    <Compile Include="Skinning\DataGridView\SkinnedDataGridViewButtonColumn.cs" />
+    <Compile Include="Skinning\DataGridView\SkinnedDataGridViewCheckBoxCell.cs" />
+    <Compile Include="Skinning\DataGridView\SkinnedDataGridViewCheckBoxColumn.cs" />
+    <Compile Include="Skinning\DataGridView\SkinnedDataGridViewComboBoxCell.cs" />
+    <Compile Include="Skinning\DataGridView\SkinnedDataGridViewComboBoxColumn.cs" />
+    <Compile Include="Skinning\DataGridView\SkinnedDataGridViewComboBoxEditingControl.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\ISkinControl.cs" />
+    <Compile Include="Skinning\Skin.cs" />
+    <Compile Include="Skinning\SkinManager.cs" />
+    <Compile Include="Skinning\SkinnedButton.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedCheckBox.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedComboBox.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedDataGridView.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedGroupBox.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedIcon.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedLabel.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedListBox.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedListView.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedMenuStrip.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedNumericUpDown.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedPanel.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedProgressBar.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedRadioButton.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedRenderer.cs" />
+    <Compile Include="Skinning\SkinnedSlider.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedSlider.Designer.cs">
+      <DependentUpon>SkinnedSlider.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Skinning\SkinnedSplitContainer.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedTabControl.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedTabStrip.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinnedTextBox.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinTester.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Skinning\SkinTester.Designer.cs">
+      <DependentUpon>SkinTester.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Skinning\VisualState.cs" />
     <Compile Include="UndoRedo\ICommand.cs" />
     <Compile Include="UndoRedo\MultiCommand.cs" />
     <Compile Include="UndoRedo\UndoManager.cs" />
     <Compile Include="Workspace.cs" />
   </ItemGroup>
   <ItemGroup>
+    <EmbeddedResource Include="CommonControls\AccordionListView.resx">
+      <DependentUpon>AccordionListView.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="CommonControls\DataSlicerControl.resx">
+      <DependentUpon>DataSlicerControl.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="CommonControls\EnumSelect.resx">
       <DependentUpon>EnumSelect.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="CommonControls\ListSelect.resx">
-      <DependentUpon>ListSelect.cs</DependentUpon>
-    </EmbeddedResource>
     <EmbeddedResource Include="CommonControls\PropertyControls\BooleanControl.resx">
       <DependentUpon>BooleanControl.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="CommonControls\PropertyControls\ColorControl.resx">
+      <DependentUpon>ColorControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="CommonControls\PropertyControls\ColorSetControl.resx">
+      <DependentUpon>ColorSetControl.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="CommonControls\PropertyControls\ComboBoxControl.resx">
       <DependentUpon>ComboBoxControl.cs</DependentUpon>
     </EmbeddedResource>
@@ -269,9 +423,6 @@
     <EmbeddedResource Include="Controls\RecordLookup.resx">
       <DependentUpon>RecordLookup.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="CommonControls\Slider.resx">
-      <DependentUpon>Slider.cs</DependentUpon>
-    </EmbeddedResource>
     <EmbeddedResource Include="Controls\WorkspaceControl.resx">
       <DependentUpon>WorkspaceControl.cs</DependentUpon>
     </EmbeddedResource>
@@ -288,9 +439,24 @@
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
+    <EmbeddedResource Include="Reporting\Controls\BooleanSlicerControl.resx">
+      <DependentUpon>BooleanSlicerControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Reporting\Controls\ComboSlicerControl.resx">
+      <DependentUpon>ComboSlicerControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Reporting\Controls\RecordSlicerControl.resx">
+      <DependentUpon>RecordSlicerControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Reporting\Controls\SlicerGroupEntry.resx">
+      <DependentUpon>SlicerGroupEntry.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Shell.resx">
       <DependentUpon>Shell.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Skinning\SkinTester.resx">
+      <DependentUpon>SkinTester.cs</DependentUpon>
+    </EmbeddedResource>
   </ItemGroup>
   <ItemGroup>
     <None Include="Icons\Help.png" />
@@ -310,6 +476,33 @@
   <ItemGroup>
     <None Include="Icons\StarOutline.png" />
   </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\Collapse.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\Expand.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\Add.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\Remove.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Icons\Pencil.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Icons\Grouped.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Icons\Ungrouped.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\DownArrow.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\UpArrow.png" />
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
diff --git a/editor source/Desktop/Extensions/NativeMethods.cs b/editor source/Desktop/Extensions/NativeMethods.cs
index 272ff50206ee52e2b45a0a38b576ac1b8b4fd5de..b9cf1e8e3c7b4ae1925f1141c331652b4222f4ed 100644
--- a/editor source/Desktop/Extensions/NativeMethods.cs	
+++ b/editor source/Desktop/Extensions/NativeMethods.cs	
@@ -7,6 +7,21 @@ public static class NativeMethods
 	[DllImport("user32.dll", CharSet = CharSet.Auto)]
 	internal static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
 
+	[DllImport("user32.dll")]
+	internal static extern bool ReleaseCapture();
+
+	[DllImport("user32.dll")]
+	internal static extern int TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y, IntPtr hwnd, IntPtr lptpm);
+
+	[DllImport("user32.dll")]
+	internal static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
+
+	[DllImport("user32.dll")]
+	internal static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
+
+	[DllImport("User32.dll", CharSet = CharSet.Auto)]
+	internal static extern bool GetMonitorInfo(HandleRef hmonitor, [In, Out] MONITORINFOEX info);
+
 	private const int WM_SETREDRAW = 11;
 	private const int WM_KEYDOWN = 0x0100;
 	private const int WM_CUT = 0x0300;
@@ -16,6 +31,36 @@ public static class NativeMethods
 
 	private const int VK_DELETE = 0x2E;
 
+	[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
+	public class MONITORINFOEX
+	{
+		public int cbSize = Marshal.SizeOf(typeof(MONITORINFOEX));
+		public RECT rcMonitor = new RECT();
+		public RECT rcWork = new RECT();
+		public int dwFlags = 0;
+		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
+		public char[] szDevice = new char[32];
+	}
+
+	[StructLayout(LayoutKind.Sequential)]
+	public struct RECT
+	{
+		public int left;
+		public int top;
+		public int right;
+		public int bottom;
+
+		public int Width()
+		{
+			return right - left;
+		}
+
+		public int Height()
+		{
+			return bottom - top;
+		}
+	}
+
 	public static void Scroll(this Control control)
 	{
 		System.Drawing.Point pt = control.PointToClient(Cursor.Position);
diff --git a/editor source/Desktop/Forms/MacroEditor.Designer.cs b/editor source/Desktop/Forms/MacroEditor.Designer.cs
index 56252d94057814183ea8be4c9faeeb5293127b4e..7ad426c4229a470162d4e373d876fd7edc70488a 100644
--- a/editor source/Desktop/Forms/MacroEditor.Designer.cs	
+++ b/editor source/Desktop/Forms/MacroEditor.Designer.cs	
@@ -29,31 +29,42 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.label1 = new System.Windows.Forms.Label();
-			this.txtName = new System.Windows.Forms.TextBox();
-			this.lblHelp = new System.Windows.Forms.Label();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.txtName = new Desktop.Skinning.SkinnedTextBox();
+			this.lblHelp = new Desktop.Skinning.SkinnedLabel();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
 			this.tableConditions = new Desktop.CommonControls.PropertyTable();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.cboMenu = new Desktop.Skinning.SkinnedComboBox();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(491, 269);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.Location = new System.Drawing.Point(746, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 3;
-			this.cmdOK.Text = "OK";
+			this.cmdOK.Text = "Accept";
 			this.cmdOK.UseVisualStyleBackColor = true;
 			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
 			// 
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(572, 269);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(827, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 4;
@@ -64,7 +75,11 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(12, 9);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(13, 37);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(38, 13);
 			this.label1.TabIndex = 3;
@@ -72,7 +87,10 @@
 			// 
 			// txtName
 			// 
-			this.txtName.Location = new System.Drawing.Point(56, 6);
+			this.txtName.BackColor = System.Drawing.Color.White;
+			this.txtName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtName.ForeColor = System.Drawing.Color.Black;
+			this.txtName.Location = new System.Drawing.Point(57, 34);
 			this.txtName.Name = "txtName";
 			this.txtName.Size = new System.Drawing.Size(371, 20);
 			this.txtName.TabIndex = 1;
@@ -80,11 +98,15 @@
 			// lblHelp
 			// 
 			this.lblHelp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.lblHelp.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblHelp.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblHelp.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
 			this.lblHelp.Image = global::Desktop.Properties.Resources.Help;
-			this.lblHelp.Location = new System.Drawing.Point(9, 274);
+			this.lblHelp.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblHelp.Location = new System.Drawing.Point(5, 4);
 			this.lblHelp.Margin = new System.Windows.Forms.Padding(3);
 			this.lblHelp.Name = "lblHelp";
-			this.lblHelp.Size = new System.Drawing.Size(14, 22);
+			this.lblHelp.Size = new System.Drawing.Size(20, 22);
 			this.lblHelp.TabIndex = 50;
 			this.toolTip1.SetToolTip(this.lblHelp, "Show help");
 			this.lblHelp.Click += new System.EventHandler(this.lblHelp_Click);
@@ -98,32 +120,81 @@
 			this.tableConditions.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.tableConditions.BackColor = System.Drawing.Color.White;
 			this.tableConditions.Data = null;
+			this.tableConditions.HeaderType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.tableConditions.HideAddField = false;
 			this.tableConditions.HideSpeedButtons = false;
-			this.tableConditions.Location = new System.Drawing.Point(12, 32);
+			this.tableConditions.Location = new System.Drawing.Point(12, 59);
+			this.tableConditions.ModifyingProperty = null;
 			this.tableConditions.Name = "tableConditions";
+			this.tableConditions.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.tableConditions.PlaceholderText = "Add a condition...";
+			this.tableConditions.PreserveControls = false;
+			this.tableConditions.PreviewData = null;
 			this.tableConditions.RemoveCaption = "Remove";
 			this.tableConditions.RowHeaderWidth = 0F;
 			this.tableConditions.RunInitialAddEvents = true;
-			this.tableConditions.Size = new System.Drawing.Size(635, 231);
+			this.tableConditions.Size = new System.Drawing.Size(881, 233);
 			this.tableConditions.Sorted = false;
 			this.tableConditions.TabIndex = 2;
+			this.tableConditions.UndoManager = null;
 			this.tableConditions.UseAutoComplete = true;
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.lblHelp);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 298);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(905, 30);
+			this.skinnedPanel1.TabIndex = 51;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.AutoSize = true;
+			this.skinnedLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel1.Location = new System.Drawing.Point(434, 37);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(37, 13);
+			this.skinnedLabel1.TabIndex = 52;
+			this.skinnedLabel1.Text = "Menu:";
+			// 
+			// cboMenu
+			// 
+			this.cboMenu.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboMenu.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboMenu.BackColor = System.Drawing.Color.White;
+			this.cboMenu.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboMenu.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboMenu.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboMenu.Location = new System.Drawing.Point(477, 33);
+			this.cboMenu.Name = "cboMenu";
+			this.cboMenu.SelectedIndex = -1;
+			this.cboMenu.SelectedItem = null;
+			this.cboMenu.Size = new System.Drawing.Size(154, 23);
+			this.cboMenu.Sorted = false;
+			this.cboMenu.TabIndex = 53;
+			// 
 			// MacroEditor
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(659, 304);
-			this.Controls.Add(this.lblHelp);
+			this.ClientSize = new System.Drawing.Size(905, 328);
+			this.Controls.Add(this.cboMenu);
+			this.Controls.Add(this.skinnedLabel1);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.txtName);
 			this.Controls.Add(this.label1);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
 			this.Controls.Add(this.tableConditions);
 			this.Name = "MacroEditor";
 			this.ShowIcon = false;
@@ -131,6 +202,7 @@
 			this.Text = "Edit Macro";
 			this.Load += new System.EventHandler(this.MacroEditor_Load);
 			this.Shown += new System.EventHandler(this.MacroEditor_Shown);
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -139,11 +211,14 @@
 		#endregion
 
 		private CommonControls.PropertyTable tableConditions;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.TextBox txtName;
-		private System.Windows.Forms.Label lblHelp;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedTextBox txtName;
+		private Desktop.Skinning.SkinnedLabel lblHelp;
 		private System.Windows.Forms.ToolTip toolTip1;
+		private Skinning.SkinnedPanel skinnedPanel1;
+		private Skinning.SkinnedLabel skinnedLabel1;
+		private Skinning.SkinnedComboBox cboMenu;
 	}
 }
\ No newline at end of file
diff --git a/editor source/Desktop/Forms/MacroEditor.cs b/editor source/Desktop/Forms/MacroEditor.cs
index 859853c59e9e8e83e1d969aa9b1c9c888326e08c..62b23e43e1d7dfe3a445f93932885bd11ce00919 100644
--- a/editor source/Desktop/Forms/MacroEditor.cs	
+++ b/editor source/Desktop/Forms/MacroEditor.cs	
@@ -1,12 +1,13 @@
 using Desktop.Forms;
 using Desktop.Providers;
+using Desktop.Skinning;
 using System;
 using System.Collections.Generic;
 using System.Windows.Forms;
 
 namespace Desktop
 {
-	public partial class MacroEditor : Form
+	public partial class MacroEditor : SkinnedForm
 	{
 		private Macro _macro;
 		private IMacroEditor _editor;
@@ -14,6 +15,8 @@ namespace Desktop
 		public MacroEditor()
 		{
 			InitializeComponent();
+
+			cboMenu.Items.AddRange(new string[] { "", "Also Playing", "Game", "Player", "Self", "Table","Target" });
 		}
 
 		public void SetMacro(Macro macro, IMacroEditor editor)
@@ -21,6 +24,7 @@ namespace Desktop
 			_macro = macro;
 			_editor = editor;
 			txtName.Text = macro.Name;
+			cboMenu.SelectedItem = macro.Group;
 		}
 
 		private void cmdOK_Click(object sender, EventArgs e)
@@ -65,6 +69,7 @@ namespace Desktop
 				_macro.Name = name;
 				provider.Add(macroType, _macro);
 			}
+			_macro.Group = cboMenu.SelectedItem?.ToString() ?? "";
 			return true;
 		}
 
@@ -93,6 +98,7 @@ namespace Desktop
 			tableConditions.Data = _editor.CreateData();
 			tableConditions.Context = _editor.GetRecordContext();
 			tableConditions.RecordFilter = _editor.GetRecordFilter(tableConditions.Data);
+			_editor.AddSpeedButtons(tableConditions);
 			tableConditions.ApplyMacro(_macro, new Dictionary<string, string>());
 		}
 	}
diff --git a/editor source/Desktop/Forms/MacroEditor.resx b/editor source/Desktop/Forms/MacroEditor.resx
index d54ad1e479b4a073f72aa823633b981956005d39..df8339b688f963f4372fdb280568769b960537d6 100644
--- a/editor source/Desktop/Forms/MacroEditor.resx	
+++ b/editor source/Desktop/Forms/MacroEditor.resx	
@@ -120,7 +120,4 @@
   <metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>17, 17</value>
   </metadata>
-  <metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>17, 17</value>
-  </metadata>
 </root>
\ No newline at end of file
diff --git a/editor source/Desktop/Forms/MacroHelp.Designer.cs b/editor source/Desktop/Forms/MacroHelp.Designer.cs
index eea178ae01dd051ffb141a2ae28124210b7ed14e..6f1dc820ba48975d43500444096c723c4df16ebf 100644
--- a/editor source/Desktop/Forms/MacroHelp.Designer.cs	
+++ b/editor source/Desktop/Forms/MacroHelp.Designer.cs	
@@ -28,8 +28,10 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.lblHelp = new System.Windows.Forms.Label();
-			this.cmdOK = new System.Windows.Forms.Button();
+			this.lblHelp = new Desktop.Skinning.SkinnedLabel();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// lblHelp
@@ -37,16 +39,20 @@
 			this.lblHelp.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblHelp.Location = new System.Drawing.Point(12, 9);
+			this.lblHelp.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblHelp.Location = new System.Drawing.Point(12, 35);
 			this.lblHelp.Name = "lblHelp";
-			this.lblHelp.Size = new System.Drawing.Size(260, 144);
+			this.lblHelp.Size = new System.Drawing.Size(260, 153);
 			this.lblHelp.TabIndex = 0;
 			this.lblHelp.Text = "Macros are shortcuts for pulling in properties.";
 			// 
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(197, 156);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.Location = new System.Drawing.Point(206, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 1;
@@ -54,25 +60,37 @@
 			this.cmdOK.UseVisualStyleBackColor = true;
 			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 195);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(284, 30);
+			this.skinnedPanel1.TabIndex = 2;
+			// 
 			// MacroHelp
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.ClientSize = new System.Drawing.Size(284, 191);
+			this.ClientSize = new System.Drawing.Size(284, 225);
 			this.ControlBox = false;
-			this.Controls.Add(this.cmdOK);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.lblHelp);
 			this.Name = "MacroHelp";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Macro Help";
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.Label lblHelp;
-		private System.Windows.Forms.Button cmdOK;
+		private Desktop.Skinning.SkinnedLabel lblHelp;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/Desktop/Forms/MacroHelp.cs b/editor source/Desktop/Forms/MacroHelp.cs
index 29b33a2861007c3b5f7ac9a66bbf469dbe56c06c..4c964ab56a9eff87681d370275c7fa11d943ce64 100644
--- a/editor source/Desktop/Forms/MacroHelp.cs	
+++ b/editor source/Desktop/Forms/MacroHelp.cs	
@@ -1,9 +1,9 @@
-using System;
-using System.Windows.Forms;
+using Desktop.Skinning;
+using System;
 
 namespace Desktop.Forms
 {
-	public partial class MacroHelp : Form
+	public partial class MacroHelp : SkinnedForm
 	{
 		public MacroHelp()
 		{
diff --git a/editor source/Desktop/Forms/VariableMapper.Designer.cs b/editor source/Desktop/Forms/VariableMapper.Designer.cs
index 64b134b4164a31b134543e410569c18d1a7a2bb9..ad1addf5fb0948f79d3334e708226d9fd06d488c 100644
--- a/editor source/Desktop/Forms/VariableMapper.Designer.cs	
+++ b/editor source/Desktop/Forms/VariableMapper.Designer.cs	
@@ -28,19 +28,25 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.gridVariables = new System.Windows.Forms.DataGridView();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.gridVariables = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColVar = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.Value = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
 			((System.ComponentModel.ISupportInitialize)(this.gridVariables)).BeginInit();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(12, 9);
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(12, 31);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(268, 13);
 			this.label1.TabIndex = 0;
@@ -55,16 +61,44 @@
 			this.gridVariables.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridVariables.BackgroundColor = System.Drawing.Color.FromArgb(((int)(((byte)(225)))), ((int)(((byte)(225)))), ((int)(((byte)(230)))));
+			this.gridVariables.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridVariables.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridVariables.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
 			this.gridVariables.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridVariables.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColVar,
             this.Value});
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle2.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridVariables.DefaultCellStyle = dataGridViewCellStyle2;
 			this.gridVariables.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
-			this.gridVariables.Location = new System.Drawing.Point(12, 25);
+			this.gridVariables.EnableHeadersVisualStyles = false;
+			this.gridVariables.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridVariables.GridColor = System.Drawing.Color.FromArgb(((int)(((byte)(142)))), ((int)(((byte)(153)))), ((int)(((byte)(243)))));
+			this.gridVariables.Location = new System.Drawing.Point(12, 48);
 			this.gridVariables.MultiSelect = false;
 			this.gridVariables.Name = "gridVariables";
+			this.gridVariables.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridVariables.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
 			this.gridVariables.RowHeadersVisible = false;
-			this.gridVariables.Size = new System.Drawing.Size(266, 102);
+			this.gridVariables.Size = new System.Drawing.Size(266, 107);
 			this.gridVariables.TabIndex = 1;
 			// 
 			// ColVar
@@ -83,18 +117,27 @@
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(122, 133);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.Blue;
+			this.cmdOK.Location = new System.Drawing.Point(131, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 2;
-			this.cmdOK.Text = "OK";
+			this.cmdOK.Text = "Apply";
 			this.cmdOK.UseVisualStyleBackColor = true;
 			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
 			// 
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdCancel.Location = new System.Drawing.Point(203, 133);
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(212, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 3;
@@ -102,22 +145,33 @@
 			this.cmdCancel.UseVisualStyleBackColor = true;
 			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 161);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(290, 30);
+			this.skinnedPanel1.TabIndex = 4;
+			// 
 			// VariableMapper
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(290, 168);
+			this.ClientSize = new System.Drawing.Size(290, 191);
 			this.ControlBox = false;
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.gridVariables);
 			this.Controls.Add(this.label1);
 			this.Name = "VariableMapper";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Fill in Variables";
 			((System.ComponentModel.ISupportInitialize)(this.gridVariables)).EndInit();
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -125,11 +179,12 @@
 
 		#endregion
 
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.DataGridView gridVariables;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedDataGridView gridVariables;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColVar;
 		private System.Windows.Forms.DataGridViewTextBoxColumn Value;
+		private Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/Desktop/Forms/VariableMapper.cs b/editor source/Desktop/Forms/VariableMapper.cs
index 7a326d4067d519a467ebb21bec56cb31d2bc34c1..c6d125d55600cf5fd6acd3c3561a85e760d739ff 100644
--- a/editor source/Desktop/Forms/VariableMapper.cs	
+++ b/editor source/Desktop/Forms/VariableMapper.cs	
@@ -1,9 +1,10 @@
-using System.Collections.Generic;
+using Desktop.Skinning;
+using System.Collections.Generic;
 using System.Windows.Forms;
 
 namespace Desktop.Forms
 {
-	public partial class VariableMapper : Form
+	public partial class VariableMapper : SkinnedForm
 	{
 		public Dictionary<string, string> Map
 		{
diff --git a/editor source/Desktop/Forms/VariableMapper.resx b/editor source/Desktop/Forms/VariableMapper.resx
index 673f1bc555b6ff6392f90d99da11792be36946c4..05e0e745f7492a789a8e7378c84c9a8deb69db82 100644
--- a/editor source/Desktop/Forms/VariableMapper.resx	
+++ b/editor source/Desktop/Forms/VariableMapper.resx	
@@ -123,10 +123,4 @@
   <metadata name="Value.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
-  <metadata name="ColVar.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>True</value>
-  </metadata>
-  <metadata name="Value.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>True</value>
-  </metadata>
 </root>
\ No newline at end of file
diff --git a/editor source/Desktop/Icons/Grouped.png b/editor source/Desktop/Icons/Grouped.png
new file mode 100644
index 0000000000000000000000000000000000000000..f5d7e8d42d7e194aed61403264a0b98fd2f19a58
--- /dev/null
+++ b/editor source/Desktop/Icons/Grouped.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c62d7bcb84d1930d32cb8f7fa1d381ebe671aec88bad1e09e1f7ed54c09dece4
+size 449
diff --git a/editor source/Desktop/Icons/Pencil.png b/editor source/Desktop/Icons/Pencil.png
new file mode 100644
index 0000000000000000000000000000000000000000..e7a4502cdf2216710c0c5a2d8f48d84f85a069f1
--- /dev/null
+++ b/editor source/Desktop/Icons/Pencil.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:48d77a7647ba887843b107a0480dc68ac954633988df0e43dad0977872ae73cf
+size 288
diff --git a/editor source/Desktop/Icons/Ungrouped.png b/editor source/Desktop/Icons/Ungrouped.png
new file mode 100644
index 0000000000000000000000000000000000000000..28d91bf94602c84a058bfb1318d14df6db005539
--- /dev/null
+++ b/editor source/Desktop/Icons/Ungrouped.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e5e5fbf3687e55254b25d3ccf933a486feb343f32d205606f15128fa61f4144a
+size 348
diff --git a/editor source/Desktop/Interfaces/IMacroEditor.cs b/editor source/Desktop/Interfaces/IMacroEditor.cs
index efebbd3bfad4eb4502cb2a1a2d0662a9878d29b5..d6cdb5c523fd908ab7230512fd6cd8451687bd67 100644
--- a/editor source/Desktop/Interfaces/IMacroEditor.cs	
+++ b/editor source/Desktop/Interfaces/IMacroEditor.cs	
@@ -1,4 +1,5 @@
-using System;
+using Desktop.CommonControls;
+using System;
 
 namespace Desktop
 {
@@ -12,5 +13,6 @@ namespace Desktop
 		object CreateData();
 		object GetRecordContext();
 		Func<PropertyRecord, bool> GetRecordFilter(object data);
+		void AddSpeedButtons(PropertyTable table);
 	}
 }
diff --git a/editor source/Desktop/Messaging/CoreDesktopMessages.cs b/editor source/Desktop/Messaging/CoreDesktopMessages.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c4b2a41a4435ffed8240d24c8f41df68ae4bf450
--- /dev/null
+++ b/editor source/Desktop/Messaging/CoreDesktopMessages.cs	
@@ -0,0 +1,10 @@
+namespace Desktop
+{
+	public static class CoreDesktopMessages
+	{
+		/// <summary>
+		/// Sent when the desktop skin has changed [Skin]
+		/// </summary>
+		public const int SkinChanged = -1;
+	}
+}
diff --git a/editor source/Desktop/Properties/Resources.Designer.cs b/editor source/Desktop/Properties/Resources.Designer.cs
index c0ab568f287dfdb2e8c29281633ada0dac4831ff..946f71d81d648fab8ca8298b41d05dc007dd7bd2 100644
--- a/editor source/Desktop/Properties/Resources.Designer.cs	
+++ b/editor source/Desktop/Properties/Resources.Designer.cs	
@@ -60,6 +60,26 @@ namespace Desktop.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Add {
+            get {
+                object obj = ResourceManager.GetObject("Add", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Collapse {
+            get {
+                object obj = ResourceManager.GetObject("Collapse", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
@@ -70,6 +90,16 @@ namespace Desktop.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap DownArrow {
+            get {
+                object obj = ResourceManager.GetObject("DownArrow", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
@@ -80,6 +110,26 @@ namespace Desktop.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Expand {
+            get {
+                object obj = ResourceManager.GetObject("Expand", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Grouped {
+            get {
+                object obj = ResourceManager.GetObject("Grouped", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
@@ -90,6 +140,26 @@ namespace Desktop.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Pencil {
+            get {
+                object obj = ResourceManager.GetObject("Pencil", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Remove {
+            get {
+                object obj = ResourceManager.GetObject("Remove", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
@@ -119,5 +189,25 @@ namespace Desktop.Properties {
                 return ((System.Drawing.Bitmap)(obj));
             }
         }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Ungrouped {
+            get {
+                object obj = ResourceManager.GetObject("Ungrouped", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap UpArrow {
+            get {
+                object obj = ResourceManager.GetObject("UpArrow", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
     }
 }
diff --git a/editor source/Desktop/Properties/Resources.resx b/editor source/Desktop/Properties/Resources.resx
index c45249bc5d6dfaf3b2c4c9baebb5ce5550d139b4..f7b3c8fe5b025d68fcc8fa58d860bf91a24ea9e0 100644
--- a/editor source/Desktop/Properties/Resources.resx	
+++ b/editor source/Desktop/Properties/Resources.resx	
@@ -118,15 +118,36 @@
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
   <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="Add" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Collapse" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Collapse.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
   <data name="Delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Icons\Delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
+  <data name="DownArrow" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\DownArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
   <data name="Eraser" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Icons\Eraser.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
+  <data name="Expand" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Expand.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Grouped" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\Grouped.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
   <data name="Help" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Icons\Help.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
+  <data name="Pencil" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\Pencil.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Remove" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Remove.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
   <data name="Search" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Icons\Search.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
@@ -136,4 +157,10 @@
   <data name="StarOutline" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Icons\StarOutline.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
+  <data name="Ungrouped" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\Ungrouped.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="UpArrow" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\UpArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
 </root>
\ No newline at end of file
diff --git a/editor source/Desktop/Providers/CategoryProvider.cs b/editor source/Desktop/Providers/CategoryProvider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d33bc091c3ccd98c84d3b9907158b83c775f9be5
--- /dev/null
+++ b/editor source/Desktop/Providers/CategoryProvider.cs	
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace Desktop.Providers
+{
+	public abstract class CategoryProvider<T> : IRecordProvider<T> where T : Category
+	{
+		private T[] _categoryValues;
+
+		public bool AllowsNew
+		{
+			get { return false; }
+		}
+
+		public bool TrackRecent
+		{
+			get { return false; }
+		}
+
+		public IRecord Create(string key)
+		{
+			throw new NotImplementedException();
+		}
+
+		public void Delete(IRecord record)
+		{
+			throw new NotImplementedException();
+		}
+
+		public ListViewItem FormatItem(IRecord record)
+		{
+			ListViewItem item = new ListViewItem(new string[] { record.Key, record.Name });
+			return item;
+		}
+
+		public string[] GetColumns()
+		{
+			return new string[] { "Key", "Value" };
+		}
+
+		public abstract string GetLookupCaption();
+
+		protected abstract T[] GetCategoryValues();
+
+		public List<IRecord> GetRecords(string text)
+		{
+			if (_categoryValues == null)
+			{
+				_categoryValues = GetCategoryValues();
+			}
+
+			text = text.ToLower();
+			List<IRecord> list = new List<IRecord>();
+			foreach (T category in _categoryValues)
+			{
+				if (category.Key.ToLower().Contains(text) || category.Name.ToLower().Contains(text))
+				{
+					list.Add(category);
+				}
+			}
+			return list;
+		}
+
+		public void SetContext(object context)
+		{
+		}
+
+		public void Sort(List<IRecord> list)
+		{
+			list.Sort();
+		}
+	}
+}
diff --git a/editor source/Desktop/Providers/SkinProvider.cs b/editor source/Desktop/Providers/SkinProvider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..516cb6efdd9a68de7c4c98ff9887665b097bf0fc
--- /dev/null
+++ b/editor source/Desktop/Providers/SkinProvider.cs	
@@ -0,0 +1,75 @@
+using Desktop.Skinning;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace Desktop.Providers
+{
+	public class SkinProvider : IRecordProvider<Skin>
+	{
+		public bool AllowsNew
+		{
+			get { return true; }
+		}
+
+		public bool TrackRecent
+		{
+			get { return false; }
+		}
+
+		public IRecord Create(string key)
+		{
+			Skin skin = new Skin();
+			skin.Name = key;
+			SkinManager.Instance.AvailableSkins.Add(skin);
+			SkinManager.Instance.AvailableSkins.Sort();
+			return skin;
+		}
+
+		public void Delete(IRecord record)
+		{
+			
+		}
+
+		public ListViewItem FormatItem(IRecord record)
+		{
+			ListViewItem item = new ListViewItem(record.Name, record.Group);
+			return item;
+		}
+
+		public string[] GetColumns()
+		{
+			return new string[] { "Name", "Group" };
+		}
+
+		public string GetLookupCaption()
+		{
+			return "Select Skin";
+		}
+
+		public List<IRecord> GetRecords(string text)
+		{
+			SkinManager skinManager = SkinManager.Instance;
+			text = text.ToLower();
+			var list = new List<IRecord>();
+			foreach (Skin record in SkinManager.Instance.AvailableSkins)
+			{
+				if (record.Key.ToLower().Contains(text) || record.Name.ToLower().Contains(text))
+				{
+					//partial match
+					list.Add(record);
+				}
+			}
+			return list;
+		}
+
+		public void SetContext(object context)
+		{
+			
+		}
+
+		public void Sort(List<IRecord> list)
+		{
+			list.Sort();
+		}
+	}
+}
diff --git a/editor source/Desktop/Providers/SlicerProvider.cs b/editor source/Desktop/Providers/SlicerProvider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..eaacffd0da3863e11f91c8ae20bd92f8cd3320a8
--- /dev/null
+++ b/editor source/Desktop/Providers/SlicerProvider.cs	
@@ -0,0 +1,113 @@
+using Desktop.Reporting;
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace Desktop.Providers
+{
+	public class SlicerDefinition : IRecord
+	{
+		public string Key { get { return Name; } set { Name = value; } }
+		public string Name { get; set; }
+		public string Group { get; set; }
+		public string Description { get; set; }
+		private Func<IDataSlicer> _creationFunc;
+
+		public SlicerDefinition(string name, string description, Func<IDataSlicer> creator)
+		{
+			Name = name;
+			Description = description;
+			_creationFunc = creator;
+		}
+
+		public IDataSlicer CreateInstance(object context)
+		{
+			IDataSlicer slicer = _creationFunc();
+			slicer.SetContext(context);
+			return slicer;
+		}
+
+		public string ToLookupString()
+		{
+			return Name;
+		}
+
+		public int CompareTo(IRecord other)
+		{
+			return Name.CompareTo(other.Name);
+		}
+	}
+
+	public class SlicerProvider : IRecordProvider<SlicerDefinition>
+	{
+		private static List<SlicerDefinition> _slicers = new List<SlicerDefinition>();
+
+		public static void AddSlicer(string name, string description, Func<IDataSlicer> creator)
+		{
+			SlicerDefinition def = new SlicerDefinition(name, description, creator);
+			_slicers.Add(def);
+		}
+
+		public bool AllowsNew
+		{
+			get { return false; }
+		}
+
+		public bool TrackRecent
+		{
+			get { return false; }
+		}
+
+		public IRecord Create(string key)
+		{
+			throw new NotImplementedException();
+		}
+
+		public void Delete(IRecord record)
+		{
+			throw new NotImplementedException();
+		}
+
+		public ListViewItem FormatItem(IRecord record)
+		{
+			SlicerDefinition slicer = record as SlicerDefinition;
+			ListViewItem item = new ListViewItem(new string[] { record.Name, slicer.Description });
+			return item;
+		}
+
+		public string[] GetColumns()
+		{
+			return new string[] { "Name", "Description" };
+		}
+
+		public string GetLookupCaption()
+		{
+			return "Slice on Data";
+		}
+
+		public List<IRecord> GetRecords(string text)
+		{
+			text = text.ToLower();
+			var list = new List<IRecord>();
+			foreach (SlicerDefinition record in _slicers)
+			{
+				if (record.Key.ToLower().Contains(text) || record.Name.ToLower().Contains(text))
+				{
+					//partial match
+					list.Add(record);
+				}
+			}
+			return list;
+		}
+
+		public void SetContext(object context)
+		{
+			
+		}
+
+		public void Sort(List<IRecord> list)
+		{
+			list.Sort();
+		}
+	}
+}
diff --git a/editor source/Desktop/Reporting/BaseSlicer.cs b/editor source/Desktop/Reporting/BaseSlicer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7e3cf7d696be3e6bfc10369f45d828602fda6fdd
--- /dev/null
+++ b/editor source/Desktop/Reporting/BaseSlicer.cs	
@@ -0,0 +1,101 @@
+using Desktop.DataStructures;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Text.RegularExpressions;
+
+namespace Desktop.Reporting
+{
+	public abstract class BaseSlicer<T> : BindableObject, IDataSlicer
+	{
+		public object Context
+		{
+			get { return Get<object>(); }
+			set { Set(value); }
+		}
+
+		public string DisplayName
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		public ObservableCollection<ISlicerGroup> Groups
+		{
+			get { return Get<ObservableCollection<ISlicerGroup>>(); }
+			set { Set(value); }
+		}
+
+		public BaseSlicer()
+		{
+			Groups = new ObservableCollection<ISlicerGroup>();
+		}
+
+		public override string ToString()
+		{
+			return DisplayName;
+		}
+
+		public ISlicerGroup AddGroup(object value)
+		{
+			SlicerGroup<T> group = new SlicerGroup<T>(value);
+			Groups.Add(group);
+			return group;
+		}
+
+		public void RemoveGroup(ISlicerGroup group)
+		{
+			Groups.Remove(group);
+		}
+
+		public void ClearGroups()
+		{
+			Groups.Clear();
+		}
+
+		public void Split(ISlicerGroup group)
+		{
+			if (group.Values.Count <= 1 || !group.Groupable)
+			{
+				return;
+			}
+			foreach (object value in group.Values)
+			{
+				AddGroup(value);
+			}
+			RemoveGroup(group);
+		}
+
+		public void Merge(ISlicerGroup source, ISlicerGroup dest)
+		{
+			foreach (object value in source.Values)
+			{
+				dest.Values.Add(value);
+			}
+			source.Values.Clear();
+			RemoveGroup(source);
+			int max = 0;
+			Regex regex = new Regex(@"Group (\d+)");
+			foreach (ISlicerGroup group in Groups)
+			{
+				Match match = regex.Match(group.Label);
+				if (match.Success)
+				{
+					int groupNum;
+					if (int.TryParse(match.Groups[2].Value, out groupNum))
+					{
+						max = Math.Max(max, groupNum);
+					}
+				}
+			}
+			dest.Label = $"Group {max + 1}";
+		}
+
+		public void SetContext(object context)
+		{
+			Context = context;
+		}
+
+		public abstract List<DataBucket> Slice(IEnumerable<ISliceable> dataSet);
+	}
+}
diff --git a/editor source/Desktop/Reporting/BooleanSlicer.cs b/editor source/Desktop/Reporting/BooleanSlicer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..cd5f95d742e3c4e50af6a26bb9b34502d493d18f
--- /dev/null
+++ b/editor source/Desktop/Reporting/BooleanSlicer.cs	
@@ -0,0 +1,117 @@
+using Desktop.CommonControls;
+using Desktop.DataStructures;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Reflection;
+
+namespace Desktop.Reporting
+{
+	public class BooleanSlicer : BindableObject, IDataSlicer
+	{
+		public string Property;
+		public string DisplayName
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		public ObservableCollection<ISlicerGroup> Groups
+		{
+			get { return Get<ObservableCollection<ISlicerGroup>>(); }
+			set { Set(value); }
+		}
+
+		public ISlicerGroup YesGroup { get; private set; }
+		public ISlicerGroup NoGroup { get; private set; }
+
+		public BooleanSlicer(string property, string displayName)
+		{
+			Groups = new ObservableCollection<ISlicerGroup>();
+			SlicerGroup<IRecord> groupTrue = new SlicerGroup<IRecord>();
+			groupTrue.Label = "Yes";
+			groupTrue.Key = "Yes";
+			groupTrue.Values.Add(true);
+			YesGroup = groupTrue;
+			Groups.Add(groupTrue);
+			SlicerGroup<IRecord> groupFalse = new SlicerGroup<IRecord>();
+			groupFalse.Label = "No";
+			groupFalse.Key = "No";
+			groupFalse.Values.Add(false);
+			NoGroup = groupFalse;
+			Groups.Add(groupFalse);
+			Property = property;
+			DisplayName = displayName;
+		}
+
+		public void SetContext(object context) { }
+
+		public override string ToString()
+		{
+			return DisplayName;
+		}
+
+		public ISlicerGroup AddGroup(object value)
+		{
+			throw new NotImplementedException();
+		}
+		public void RemoveGroup(ISlicerGroup group)
+		{
+			throw new NotImplementedException();
+		}
+		public void Split(ISlicerGroup group)
+		{
+			throw new NotImplementedException();
+		}
+		public void Merge(ISlicerGroup source, ISlicerGroup dest)
+		{
+			throw new NotImplementedException();
+		}
+
+		public List<DataBucket> Slice(IEnumerable<ISliceable> dataSet)
+		{
+			Dictionary<bool, DataBucket> buckets = new Dictionary<bool, DataBucket>();
+			List<DataBucket> list = new List<DataBucket>();
+			foreach (SlicerGroup<IRecord> group in Groups)
+			{
+				if (!group.Active) { continue; }
+				DataBucket bucket = new DataBucket(group.Label);
+				foreach (bool record in group.Values)
+				{
+					buckets[record] = bucket;
+				}
+				list.Add(bucket);
+			}
+			list.Sort();
+
+			foreach (ISliceable obj in dataSet)
+			{
+				MemberInfo mi = PropertyTypeInfo.GetMemberInfo(obj.GetType(), Property);
+				object value = mi.GetValue(obj);
+				bool result = false;
+				if (value is bool)
+				{
+					result = (bool)value;
+				}
+				else if (value is string)
+				{
+					result = (value?.ToString() == "1");
+				}
+				DataBucket bucket = null;
+
+				if (buckets.ContainsKey(result))
+				{
+					bucket = buckets[result];
+				}
+				
+				if (bucket != null)
+				{
+					bucket.Count += obj.GetSliceCount();
+					bucket.Data.Add(obj);
+				}
+			}
+
+			return list;
+		}
+	}
+}
diff --git a/editor source/Desktop/Reporting/ComboSlicer.cs b/editor source/Desktop/Reporting/ComboSlicer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e87723914eb7fdc89930b56794e474a1db6cfdc5
--- /dev/null
+++ b/editor source/Desktop/Reporting/ComboSlicer.cs	
@@ -0,0 +1,103 @@
+using Desktop.CommonControls;
+using Desktop.CommonControls.PropertyControls;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+namespace Desktop.Reporting
+{
+	public class ComboSlicer : BaseSlicer<string>
+	{
+		public ISlicerGroup AnyGroup { get; private set; }
+		public string Property;
+
+		public ComboSlicer(Type sourceType, string property, string displayName, KeyValuePair<string, string>[] options = null)
+		{
+			DisplayName = displayName;
+			Property = property;
+			if (options == null)
+			{
+				MemberInfo mi = PropertyTypeInfo.GetMemberInfo(sourceType, property);
+				ComboBoxAttribute attrib = mi.GetCustomAttribute<ComboBoxAttribute>();
+				if (attrib != null)
+				{
+					foreach (string value in attrib.Options)
+					{
+						if (!string.IsNullOrEmpty(value))
+						{
+							ISlicerGroup group = AddGroup(new KeyValuePair<string, string>(value, value));
+							group.Label = value;
+							group.Active = false;
+						}
+					}
+				}
+			}
+			else
+			{
+				foreach (KeyValuePair<string, string> kvp in options)
+				{
+					if (!string.IsNullOrEmpty(kvp.Key))
+					{
+						ISlicerGroup group = AddGroup(kvp);
+						group.Label = kvp.Value;
+						group.Active = false;
+					}
+				}
+			}
+			AnyGroup = AddGroup("-");
+			AnyGroup.Label = "Not specified";
+		}
+
+		public override List<DataBucket> Slice(IEnumerable<ISliceable> dataSet)
+		{
+			Dictionary<string, DataBucket> buckets = new Dictionary<string, DataBucket>();
+			List<DataBucket> list = new List<DataBucket>();
+			DataBucket catchNull = null;
+			foreach (ISlicerGroup group in Groups)
+			{
+				if (!group.Active) { continue; }
+				DataBucket bucket = new DataBucket(group.Label);
+				if (group.Key == "-")
+				{
+					catchNull = bucket;
+					continue;
+				}
+				foreach (KeyValuePair<string, string> value in group.Values)
+				{
+					if (string.IsNullOrEmpty(value.Key)) { continue; }
+					buckets[value.Key] = bucket;
+				}
+				list.Add(bucket);
+			}
+			list.Sort();
+			if (catchNull != null)
+			{
+				list.Add(catchNull);
+			}
+
+			foreach (ISliceable obj in dataSet)
+			{
+				MemberInfo mi = PropertyTypeInfo.GetMemberInfo(obj.GetType(), Property);
+				string value = mi.GetValue(obj)?.ToString();
+				DataBucket bucket = null;
+				if (!string.IsNullOrEmpty(value))
+				{
+					if (buckets.ContainsKey(value))
+					{
+						bucket = buckets[value];
+					}
+				}
+				else
+				{
+					bucket = catchNull;
+				}
+				if (bucket != null)
+				{
+					bucket.Count += obj.GetSliceCount();
+					bucket.Data.Add(obj);
+				}
+			}
+
+			return list;
+		}
+	}
+}
diff --git a/editor source/Desktop/Reporting/Controls/BooleanSlicerControl.Designer.cs b/editor source/Desktop/Reporting/Controls/BooleanSlicerControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e90332ad30084479cd4684a32a1f71797172c58a
--- /dev/null
+++ b/editor source/Desktop/Reporting/Controls/BooleanSlicerControl.Designer.cs	
@@ -0,0 +1,76 @@
+namespace Desktop.Reporting.Controls
+{
+	partial class BooleanSlicerControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.chkYes = new Desktop.Skinning.SkinnedCheckBox();
+			this.chkNo = new Desktop.Skinning.SkinnedCheckBox();
+			this.SuspendLayout();
+			// 
+			// chkYes
+			// 
+			this.chkYes.AutoSize = true;
+			this.chkYes.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkYes.Location = new System.Drawing.Point(3, 3);
+			this.chkYes.Name = "chkYes";
+			this.chkYes.Size = new System.Drawing.Size(44, 17);
+			this.chkYes.TabIndex = 0;
+			this.chkYes.Text = "Yes";
+			this.chkYes.UseVisualStyleBackColor = true;
+			this.chkYes.CheckedChanged += new System.EventHandler(this.chkYes_CheckedChanged);
+			// 
+			// chkNo
+			// 
+			this.chkNo.AutoSize = true;
+			this.chkNo.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkNo.Location = new System.Drawing.Point(3, 26);
+			this.chkNo.Name = "chkNo";
+			this.chkNo.Size = new System.Drawing.Size(40, 17);
+			this.chkNo.TabIndex = 1;
+			this.chkNo.Text = "No";
+			this.chkNo.UseVisualStyleBackColor = true;
+			this.chkNo.CheckedChanged += new System.EventHandler(this.chkNo_CheckedChanged);
+			// 
+			// BooleanSlicerControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.chkNo);
+			this.Controls.Add(this.chkYes);
+			this.Name = "BooleanSlicerControl";
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Skinning.SkinnedCheckBox chkYes;
+		private Skinning.SkinnedCheckBox chkNo;
+	}
+}
diff --git a/editor source/Desktop/Reporting/Controls/BooleanSlicerControl.cs b/editor source/Desktop/Reporting/Controls/BooleanSlicerControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4ea7ebb2ac4b3693c069739c5db612ae0cd8cd2a
--- /dev/null
+++ b/editor source/Desktop/Reporting/Controls/BooleanSlicerControl.cs	
@@ -0,0 +1,44 @@
+using System;
+using System.Windows.Forms;
+
+namespace Desktop.Reporting.Controls
+{
+	[DataSlicerControl(typeof(BooleanSlicer))]
+	public partial class BooleanSlicerControl : UserControl, ISlicerControl
+	{
+		private BooleanSlicer _slicer;
+
+		public BooleanSlicerControl()
+		{
+			InitializeComponent();
+		}
+
+		public void SetSlicer(IDataSlicer slicer)
+		{
+			_slicer = slicer as BooleanSlicer;
+			foreach (ISlicerGroup group in _slicer.Groups)
+			{
+				if (group.Label == "Yes")
+				{
+					chkYes.Checked = group.Active;
+				}
+				else if (group.Label == "No")
+				{
+					chkNo.Checked = group.Active;
+				}
+			}
+		}
+
+		private void chkYes_CheckedChanged(object sender, EventArgs e)
+		{
+			ISlicerGroup group = _slicer.YesGroup;
+			group.Active = chkYes.Checked;
+		}
+
+		private void chkNo_CheckedChanged(object sender, EventArgs e)
+		{
+			ISlicerGroup group = _slicer.NoGroup;
+			group.Active = chkNo.Checked;
+		}
+	}
+}
diff --git a/editor source/Desktop/CommonControls/ListSelect.resx b/editor source/Desktop/Reporting/Controls/BooleanSlicerControl.resx
similarity index 100%
rename from editor source/Desktop/CommonControls/ListSelect.resx
rename to editor source/Desktop/Reporting/Controls/BooleanSlicerControl.resx
diff --git a/editor source/Desktop/Reporting/Controls/ComboSlicerControl.Designer.cs b/editor source/Desktop/Reporting/Controls/ComboSlicerControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e19148a62f8c48a6643caca8fdb9ebd44c2862cd
--- /dev/null
+++ b/editor source/Desktop/Reporting/Controls/ComboSlicerControl.Designer.cs	
@@ -0,0 +1,69 @@
+namespace Desktop.Reporting.Controls
+{
+	partial class ComboSlicerControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.components = new System.ComponentModel.Container();
+			this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
+			this.mnuMerge = new System.Windows.Forms.ContextMenuStrip(this.components);
+			this.SuspendLayout();
+			// 
+			// flowPanel
+			// 
+			this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.flowPanel.AutoScroll = true;
+			this.flowPanel.Location = new System.Drawing.Point(3, 0);
+			this.flowPanel.Name = "flowPanel";
+			this.flowPanel.Size = new System.Drawing.Size(325, 150);
+			this.flowPanel.TabIndex = 3;
+			// 
+			// mnuMerge
+			// 
+			this.mnuMerge.Name = "mnuMerge";
+			this.mnuMerge.Size = new System.Drawing.Size(61, 4);
+			this.mnuMerge.Tag = "Surface";
+			// 
+			// ComboSlicerControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.flowPanel);
+			this.Name = "ComboSlicerControl";
+			this.Size = new System.Drawing.Size(331, 150);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private System.Windows.Forms.FlowLayoutPanel flowPanel;
+		private System.Windows.Forms.ContextMenuStrip mnuMerge;
+	}
+}
diff --git a/editor source/Desktop/Reporting/Controls/ComboSlicerControl.cs b/editor source/Desktop/Reporting/Controls/ComboSlicerControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f7b1f35990e21a73962c0d0614586481b4b153bd
--- /dev/null
+++ b/editor source/Desktop/Reporting/Controls/ComboSlicerControl.cs	
@@ -0,0 +1,120 @@
+using System;
+using System.Windows.Forms;
+
+namespace Desktop.Reporting.Controls
+{
+	[DataSlicerControl(typeof(ComboSlicer))]
+	public partial class ComboSlicerControl : UserControl, ISlicerControl
+	{
+		private IDataSlicer _slicer;
+
+		public ComboSlicerControl()
+		{
+			InitializeComponent();
+		}
+
+		public void SetSlicer(IDataSlicer slicer)
+		{
+			_slicer = slicer;
+			_slicer.Groups.CollectionChanged += Groups_CollectionChanged;
+			foreach (ISlicerGroup group in _slicer.Groups)
+			{
+				AddEntry(group);
+			}
+		}
+
+		private void Groups_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+		{
+			switch (e.Action)
+			{
+				case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
+					ISlicerGroup group = e.NewItems[0] as ISlicerGroup;
+					AddEntry(group);
+					break;
+				case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
+					group = e.OldItems[0] as ISlicerGroup;
+					foreach (Control ctl in flowPanel.Controls)
+					{
+						SlicerGroupEntry entry = ctl as SlicerGroupEntry;
+						if (entry != null && entry.Group == group)
+						{
+							DeleteEntry(entry);
+							break;
+						}
+					}
+					break;
+			}
+		}
+
+		private void AddEntry(ISlicerGroup group)
+		{
+			SlicerGroupEntry entry = new SlicerGroupEntry();
+			entry.Delete += Entry_Delete;
+			entry.Merge += Entry_Merge;
+			entry.Removable = false;
+			entry.SetGroup(group);
+			entry.Anchor = AnchorStyles.Left | AnchorStyles.Right;
+			entry.Width = flowPanel.Width - flowPanel.Padding.Left - flowPanel.Padding.Right;
+			flowPanel.Controls.Add(entry);
+		}
+
+		private void Entry_Delete(object sender, EventArgs e)
+		{
+			SlicerGroupEntry entry = sender as SlicerGroupEntry;
+			entry.Group.Active = false;
+			_slicer.RemoveGroup(entry.Group);
+		}
+
+		private void DeleteEntry(SlicerGroupEntry entry)
+		{
+			entry.Delete -= Entry_Delete;
+			entry.Merge -= Entry_Merge;
+			flowPanel.Controls.Remove(entry);
+			_slicer.RemoveGroup(entry.Group);
+		}
+
+		private void Entry_Merge(object sender, EventArgs e)
+		{
+			mnuMerge.Items.Clear();
+			SlicerGroupEntry entry = sender as SlicerGroupEntry;
+			if (entry.Group.Values.Count > 1)
+			{
+				ToolStripItem item = mnuMerge.Items.Add("Split apart");
+				item.Tag = entry;
+				item.Click += Item_Split;
+			}
+			else
+			{
+				foreach (ISlicerGroup group in _slicer.Groups)
+				{
+					if (entry.Group != group && group.Groupable)
+					{
+						ToolStripItem item = mnuMerge.Items.Add($"Merge into {group.Label}");
+						item.Tag = new Tuple<SlicerGroupEntry, ISlicerGroup>(entry, group);
+						item.Click += Item_Click;
+					}
+				}
+			}
+			mnuMerge.Show(MousePosition);
+		}
+
+		private void Item_Split(object sender, EventArgs e)
+		{
+			ToolStripItem item = sender as ToolStripItem;
+			SlicerGroupEntry entry = item.Tag as SlicerGroupEntry;
+			ISlicerGroup group = entry.Group;
+			_slicer.Split(group);
+		}
+
+		private void Item_Click(object sender, EventArgs e)
+		{
+			ToolStripItem item = sender as ToolStripItem;
+			Tuple<SlicerGroupEntry, ISlicerGroup> data = item.Tag as Tuple<SlicerGroupEntry, ISlicerGroup>;
+			SlicerGroupEntry sourceEntry = data.Item1;
+			ISlicerGroup sourceGroup = sourceEntry.Group;
+			ISlicerGroup destGroup = data.Item2;
+			_slicer.Merge(sourceGroup, destGroup);
+			DeleteEntry(sourceEntry);
+		}
+	}
+}
diff --git a/editor source/Desktop/Reporting/Controls/ComboSlicerControl.resx b/editor source/Desktop/Reporting/Controls/ComboSlicerControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..ecb79505bd8051bed0db1bb94c3e8b6540d554f1
--- /dev/null
+++ b/editor source/Desktop/Reporting/Controls/ComboSlicerControl.resx	
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="mnuMerge.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/Desktop/Reporting/Controls/RecordSlicerControl.Designer.cs b/editor source/Desktop/Reporting/Controls/RecordSlicerControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5a136e67e4e56ab05b5295403eb671f648b13c67
--- /dev/null
+++ b/editor source/Desktop/Reporting/Controls/RecordSlicerControl.Designer.cs	
@@ -0,0 +1,88 @@
+namespace Desktop.Reporting.Controls
+{
+	partial class RecordSlicerControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.components = new System.ComponentModel.Container();
+			this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
+			this.mnuMerge = new System.Windows.Forms.ContextMenuStrip(this.components);
+			this.recField = new Desktop.CommonControls.RecordField();
+			this.SuspendLayout();
+			// 
+			// flowPanel
+			// 
+			this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.flowPanel.AutoScroll = true;
+			this.flowPanel.Location = new System.Drawing.Point(0, 31);
+			this.flowPanel.Name = "flowPanel";
+			this.flowPanel.Size = new System.Drawing.Size(313, 177);
+			this.flowPanel.TabIndex = 2;
+			// 
+			// mnuMerge
+			// 
+			this.mnuMerge.Name = "mnuMerge";
+			this.mnuMerge.Size = new System.Drawing.Size(61, 4);
+			this.mnuMerge.Tag = "Surface";
+			// 
+			// recField
+			// 
+			this.recField.AllowCreate = false;
+			this.recField.Location = new System.Drawing.Point(3, 3);
+			this.recField.Name = "recField";
+			this.recField.PlaceholderText = null;
+			this.recField.Record = null;
+			this.recField.RecordContext = null;
+			this.recField.RecordFilter = null;
+			this.recField.RecordKey = null;
+			this.recField.RecordType = null;
+			this.recField.Size = new System.Drawing.Size(150, 20);
+			this.recField.TabIndex = 0;
+			this.recField.UseAutoComplete = false;
+			this.recField.RecordChanged += new System.EventHandler<Desktop.CommonControls.RecordEventArgs>(this.recField_RecordChanged);
+			// 
+			// RecordSlicerControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.recField);
+			this.Controls.Add(this.flowPanel);
+			this.Name = "RecordSlicerControl";
+			this.Size = new System.Drawing.Size(313, 208);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private System.Windows.Forms.FlowLayoutPanel flowPanel;
+		private CommonControls.RecordField recField;
+		private System.Windows.Forms.ContextMenuStrip mnuMerge;
+	}
+}
diff --git a/editor source/Desktop/Reporting/Controls/RecordSlicerControl.cs b/editor source/Desktop/Reporting/Controls/RecordSlicerControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f408c245e9d5a1aa20bb4fe80b2c11b6a52e1490
--- /dev/null
+++ b/editor source/Desktop/Reporting/Controls/RecordSlicerControl.cs	
@@ -0,0 +1,144 @@
+using System;
+using System.Windows.Forms;
+
+namespace Desktop.Reporting.Controls
+{
+	[DataSlicerControl(typeof(RecordSlicer))]
+	public partial class RecordSlicerControl : UserControl, ISlicerControl
+	{
+		private RecordSlicer _slicer;
+
+		public Type RecordType
+		{
+			get { return recField.RecordType; }
+			set { recField.RecordType = value; }
+		}
+
+		public RecordSlicerControl()
+		{
+			InitializeComponent();
+		}
+
+		public void SetSlicer(IDataSlicer slicer)
+		{
+			_slicer = slicer as RecordSlicer;
+			if (_slicer != null)
+			{
+				_slicer.Groups.CollectionChanged += Groups_CollectionChanged;
+				RecordType = _slicer.RecordType;
+				recField.RecordContext = _slicer.Context;
+				foreach (SlicerGroup<IRecord> group in _slicer.Groups)
+				{
+					AddEntry(group);
+				}
+			}
+		}
+
+		private void Groups_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+		{
+			switch (e.Action)
+			{
+				case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
+					ISlicerGroup group = e.NewItems[0] as ISlicerGroup;
+					AddEntry(group);
+					break;
+				case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
+					group = e.OldItems[0] as ISlicerGroup;
+					foreach (Control ctl in flowPanel.Controls)
+					{
+						SlicerGroupEntry entry = ctl as SlicerGroupEntry;
+						if (entry != null && entry.Group == group)
+						{
+							DeleteEntry(entry);
+							break;
+						}
+					}
+					break;
+			}
+		}
+
+		private void recField_RecordChanged(object sender, CommonControls.RecordEventArgs e)
+		{
+			if (recField.Record != null)
+			{
+				AddEntry(recField.Record);
+				recField.RecordKey = null;
+			}
+		}
+
+		private void AddEntry(IRecord record)
+		{
+			ISlicerGroup group = _slicer.AddGroup(record);
+		}
+
+		private void AddEntry(ISlicerGroup group)
+		{
+			SlicerGroupEntry entry = new SlicerGroupEntry();
+			entry.Delete += Entry_Delete;
+			entry.Merge += Entry_Merge;
+			entry.SetGroup(group);
+			entry.Anchor = AnchorStyles.Left | AnchorStyles.Right;
+			entry.Width = flowPanel.Width - flowPanel.Padding.Left - flowPanel.Padding.Right;
+			flowPanel.Controls.Add(entry);
+		}
+
+		private void Entry_Delete(object sender, EventArgs e)
+		{
+			SlicerGroupEntry entry = sender as SlicerGroupEntry;
+			_slicer.RemoveGroup(entry.Group);
+			entry.Group.Active = false;
+		}
+
+		private void DeleteEntry(SlicerGroupEntry entry)
+		{
+			entry.Delete -= Entry_Delete;
+			entry.Merge -= Entry_Merge;
+			flowPanel.Controls.Remove(entry);
+			_slicer.RemoveGroup(entry.Group);
+		}
+
+		private void Entry_Merge(object sender, EventArgs e)
+		{
+			mnuMerge.Items.Clear();
+			SlicerGroupEntry entry = sender as SlicerGroupEntry;
+			if (entry.Group.Values.Count > 1)
+			{
+				ToolStripItem item = mnuMerge.Items.Add("Split apart");
+				item.Tag = entry;
+				item.Click += Item_Split;
+			}
+			else
+			{
+				foreach (ISlicerGroup group in _slicer.Groups)
+				{
+					if (entry.Group != group && group.Groupable)
+					{
+						ToolStripItem item = mnuMerge.Items.Add($"Merge into {group.Label}");
+						item.Tag = new Tuple<SlicerGroupEntry, ISlicerGroup>(entry, group);
+						item.Click += Item_Click;
+					}
+				}
+			}
+			mnuMerge.Show(MousePosition);
+		}
+
+		private void Item_Split(object sender, EventArgs e)
+		{
+			ToolStripItem item = sender as ToolStripItem;
+			SlicerGroupEntry entry = item.Tag as SlicerGroupEntry;
+			ISlicerGroup group = entry.Group;
+			_slicer.Split(group);
+		}
+
+		private void Item_Click(object sender, EventArgs e)
+		{
+			ToolStripItem item = sender as ToolStripItem;
+			Tuple<SlicerGroupEntry, ISlicerGroup> data = item.Tag as Tuple<SlicerGroupEntry, ISlicerGroup>;
+			SlicerGroupEntry sourceEntry = data.Item1;
+			ISlicerGroup sourceGroup = sourceEntry.Group;
+			ISlicerGroup destGroup = data.Item2;
+			_slicer.Merge(sourceGroup, destGroup);
+			DeleteEntry(sourceEntry);
+		}
+	}
+}
diff --git a/editor source/Desktop/Reporting/Controls/RecordSlicerControl.resx b/editor source/Desktop/Reporting/Controls/RecordSlicerControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..ecb79505bd8051bed0db1bb94c3e8b6540d554f1
--- /dev/null
+++ b/editor source/Desktop/Reporting/Controls/RecordSlicerControl.resx	
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="mnuMerge.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/Desktop/Reporting/Controls/SlicerGroupEntry.Designer.cs b/editor source/Desktop/Reporting/Controls/SlicerGroupEntry.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..99d3870f08d13adbe5c35e528bb4108b7bf61ee1
--- /dev/null
+++ b/editor source/Desktop/Reporting/Controls/SlicerGroupEntry.Designer.cs	
@@ -0,0 +1,144 @@
+namespace Desktop.Reporting.Controls
+{
+	partial class SlicerGroupEntry
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.lblEntry = new System.Windows.Forms.Label();
+			this.cmdGroup = new Desktop.Skinning.SkinnedIcon();
+			this.txtGroupName = new Desktop.Skinning.SkinnedTextBox();
+			this.cmdDelete = new Desktop.Skinning.SkinnedIcon();
+			this.chkActive = new Desktop.Skinning.SkinnedCheckBox();
+			this.cmdEdit = new Desktop.Skinning.SkinnedIcon();
+			this.SuspendLayout();
+			// 
+			// lblEntry
+			// 
+			this.lblEntry.AutoSize = true;
+			this.lblEntry.Location = new System.Drawing.Point(44, 4);
+			this.lblEntry.Name = "lblEntry";
+			this.lblEntry.Size = new System.Drawing.Size(31, 13);
+			this.lblEntry.TabIndex = 1;
+			this.lblEntry.Text = "Entry";
+			this.lblEntry.TextChanged += new System.EventHandler(this.lblEntry_TextChanged);
+			// 
+			// cmdGroup
+			// 
+			this.cmdGroup.Anchor = System.Windows.Forms.AnchorStyles.Left;
+			this.cmdGroup.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdGroup.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdGroup.Flat = false;
+			this.cmdGroup.Image = global::Desktop.Properties.Resources.Ungrouped;
+			this.cmdGroup.Location = new System.Drawing.Point(19, 3);
+			this.cmdGroup.Name = "cmdGroup";
+			this.cmdGroup.Size = new System.Drawing.Size(16, 16);
+			this.cmdGroup.TabIndex = 1;
+			this.cmdGroup.UseVisualStyleBackColor = true;
+			this.cmdGroup.Click += new System.EventHandler(this.cmdGroup_Click);
+			// 
+			// txtGroupName
+			// 
+			this.txtGroupName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtGroupName.BackColor = System.Drawing.Color.White;
+			this.txtGroupName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtGroupName.ForeColor = System.Drawing.Color.Black;
+			this.txtGroupName.Location = new System.Drawing.Point(41, 1);
+			this.txtGroupName.Name = "txtGroupName";
+			this.txtGroupName.Size = new System.Drawing.Size(204, 20);
+			this.txtGroupName.TabIndex = 2;
+			this.txtGroupName.Visible = false;
+			this.txtGroupName.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txtGroupName_KeyDown);
+			this.txtGroupName.Leave += new System.EventHandler(this.txtGroupName_Leave);
+			this.txtGroupName.Validated += new System.EventHandler(this.txtGroupName_Validated);
+			// 
+			// cmdDelete
+			// 
+			this.cmdDelete.Anchor = System.Windows.Forms.AnchorStyles.Right;
+			this.cmdDelete.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdDelete.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdDelete.Flat = false;
+			this.cmdDelete.Image = global::Desktop.Properties.Resources.Delete;
+			this.cmdDelete.Location = new System.Drawing.Point(251, 3);
+			this.cmdDelete.Name = "cmdDelete";
+			this.cmdDelete.Size = new System.Drawing.Size(16, 16);
+			this.cmdDelete.TabIndex = 4;
+			this.cmdDelete.UseVisualStyleBackColor = true;
+			this.cmdDelete.Click += new System.EventHandler(this.cmdDelete_Click);
+			// 
+			// chkActive
+			// 
+			this.chkActive.AutoSize = true;
+			this.chkActive.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkActive.Location = new System.Drawing.Point(0, 4);
+			this.chkActive.Name = "chkActive";
+			this.chkActive.Size = new System.Drawing.Size(15, 14);
+			this.chkActive.TabIndex = 0;
+			this.chkActive.UseVisualStyleBackColor = true;
+			this.chkActive.CheckedChanged += new System.EventHandler(this.chkActive_CheckedChanged);
+			// 
+			// cmdEdit
+			// 
+			this.cmdEdit.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdEdit.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdEdit.Flat = false;
+			this.cmdEdit.Image = global::Desktop.Properties.Resources.Pencil;
+			this.cmdEdit.Location = new System.Drawing.Point(57, 3);
+			this.cmdEdit.Name = "cmdEdit";
+			this.cmdEdit.Size = new System.Drawing.Size(16, 16);
+			this.cmdEdit.TabIndex = 3;
+			this.cmdEdit.Text = "Edit";
+			this.cmdEdit.UseVisualStyleBackColor = true;
+			this.cmdEdit.Click += new System.EventHandler(this.cmdEdit_Click);
+			// 
+			// SlicerGroupEntry
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.cmdGroup);
+			this.Controls.Add(this.txtGroupName);
+			this.Controls.Add(this.cmdDelete);
+			this.Controls.Add(this.lblEntry);
+			this.Controls.Add(this.chkActive);
+			this.Controls.Add(this.cmdEdit);
+			this.Name = "SlicerGroupEntry";
+			this.Size = new System.Drawing.Size(270, 21);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Skinning.SkinnedCheckBox chkActive;
+		private System.Windows.Forms.Label lblEntry;
+		private Skinning.SkinnedIcon cmdDelete;
+		private Skinning.SkinnedTextBox txtGroupName;
+		private Skinning.SkinnedIcon cmdEdit;
+		private Skinning.SkinnedIcon cmdGroup;
+	}
+}
diff --git a/editor source/Desktop/Reporting/Controls/SlicerGroupEntry.cs b/editor source/Desktop/Reporting/Controls/SlicerGroupEntry.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a2d114902e0a933aad5bd6327923da9a383f6cd8
--- /dev/null
+++ b/editor source/Desktop/Reporting/Controls/SlicerGroupEntry.cs	
@@ -0,0 +1,105 @@
+using System;
+using System.Windows.Forms;
+
+namespace Desktop.Reporting.Controls
+{
+	public partial class SlicerGroupEntry : UserControl
+	{
+		public event EventHandler Delete;
+		public event EventHandler Merge;
+
+		public ISlicerGroup Group { get; private set; }
+		public bool Removable
+		{
+			get { return cmdDelete.Visible; }
+			set { cmdDelete.Visible = value; }
+		}
+
+		public SlicerGroupEntry()
+		{
+			InitializeComponent();
+		}
+
+		public void SetGroup(ISlicerGroup group)
+		{
+			Group = group;
+			Group.PropertyChanged += Group_PropertyChanged;
+			lblEntry.Text = group.Label;
+			chkActive.Checked = group.Active;
+			cmdEdit.Left = lblEntry.Right + 5;
+			cmdGroup.Visible = group.Groupable;
+		}
+
+		private void Group_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			if (e.PropertyName == "Label" && lblEntry.Text != Group.Label)
+			{
+				lblEntry.Text = Group.Label;
+				RepositionPencil();
+			}
+			else if (e.PropertyName == "Values")
+			{
+				if (Group.Values.Count > 1)
+				{
+					cmdGroup.Image = Properties.Resources.Grouped;
+				}
+				else
+				{
+					cmdGroup.Image = Properties.Resources.Ungrouped;
+				}
+			}
+		}
+
+		private void RepositionPencil()
+		{
+			cmdEdit.Left = lblEntry.Right + 5;
+		}
+
+		private void lblEntry_TextChanged(object sender, EventArgs e)
+		{
+			RepositionPencil();
+		}
+
+		private void cmdEdit_Click(object sender, EventArgs e)
+		{
+			txtGroupName.Text = lblEntry.Text;
+			txtGroupName.Visible = true;
+			txtGroupName.Focus();
+		}
+
+		private void txtGroupName_Leave(object sender, EventArgs e)
+		{
+			lblEntry.Text = txtGroupName.Text;
+			txtGroupName.Visible = false;
+		}
+
+		private void cmdDelete_Click(object sender, EventArgs e)
+		{
+			Delete?.Invoke(this, EventArgs.Empty);
+		}
+
+		private void chkActive_CheckedChanged(object sender, EventArgs e)
+		{
+			Group.Active = chkActive.Checked;
+		}
+
+		private void txtGroupName_Validated(object sender, EventArgs e)
+		{
+			Group.Label = txtGroupName.Text;
+		}
+
+		private void cmdGroup_Click(object sender, EventArgs e)
+		{
+			Merge?.Invoke(this, EventArgs.Empty);
+		}
+
+		private void txtGroupName_KeyDown(object sender, KeyEventArgs e)
+		{
+			if (e.KeyCode == Keys.Enter)
+			{
+				Group.Label = txtGroupName.Text;
+				cmdEdit.Focus();
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/CommonControls/Slider.resx b/editor source/Desktop/Reporting/Controls/SlicerGroupEntry.resx
similarity index 100%
rename from editor source/Desktop/CommonControls/Slider.resx
rename to editor source/Desktop/Reporting/Controls/SlicerGroupEntry.resx
diff --git a/editor source/Desktop/Reporting/DataSlicer.cs b/editor source/Desktop/Reporting/DataSlicer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..065731078ecb0826c73bc1cffefab8240d3cd055
--- /dev/null
+++ b/editor source/Desktop/Reporting/DataSlicer.cs	
@@ -0,0 +1,126 @@
+using Desktop.DataStructures;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+
+namespace Desktop.Reporting
+{
+	public class DataSlicer : BindableObject
+	{
+		public object Context
+		{
+			get { return Get<object>(); }
+			set { Set(value); }
+		}
+
+		public ObservableCollection<IDataSlicer> Slicers
+		{
+			get { return Get<ObservableCollection<IDataSlicer>>(); }
+			set { Set(value); }
+		}
+
+		public DataSlicer(object context)
+		{
+			Slicers = new ObservableCollection<IDataSlicer>();
+			Context = context;
+		}
+
+		public void AddSlicer(IDataSlicer slicer)
+		{
+			Slicers.Add(slicer);
+		}
+
+		public void RemoveSlicer(IDataSlicer slicer)
+		{
+			Slicers.Remove(slicer);
+		}
+
+		public void MoveSlicer(IDataSlicer slicer, int direction)
+		{
+			direction = Math.Sign(direction);
+			int index = Slicers.IndexOf(slicer);
+			if (direction == -1 && index > 0)
+			{
+				Slicers.RemoveAt(index);
+				Slicers.Insert(index - 1, slicer);
+			}
+			else if (direction == 1 && index < Slicers.Count - 1)
+			{
+				Slicers.RemoveAt(index);
+				Slicers.Insert(index + 1, slicer);
+			}
+		}
+
+		public List<DataBucket> Slice(IEnumerable<ISliceable> unslicedData)
+		{
+			if (Slicers.Count == 0)
+			{
+				return new List<DataBucket>(); ;
+			}
+
+			return Slice(0, unslicedData);
+		}
+
+		private List<DataBucket> Slice(int index, IEnumerable<ISliceable> dataSet)
+		{
+			List<DataBucket> buckets = Slicers[index].Slice(dataSet);
+			if (index < Slicers.Count - 1)
+			{
+				foreach (DataBucket bucket in buckets)
+				{
+					bucket.Count = 0;
+					List<DataBucket> sublist = Slice(index + 1, bucket.Data);
+					foreach (DataBucket sub in sublist)
+					{
+						bucket.Count = Math.Max(bucket.Count, sub.Count);
+						bucket.SubBuckets.Add(sub);
+					}
+				}
+			}
+			return buckets;
+		}
+	}
+
+	public class DataBucket : IComparable<DataBucket>
+	{
+		public List<ISliceable> Data = new List<ISliceable>();
+
+		public string Name;
+		public int Count;
+
+		public DataBucket(string name)
+		{
+			Name = name;
+		}
+
+		public List<DataBucket> SubBuckets = new List<DataBucket>();
+
+		public override string ToString()
+		{
+			return $"{Name} ({Count})";
+		}
+
+		public int CompareTo(DataBucket other)
+		{
+			return Name.CompareTo(other.Name);
+		}
+	}
+
+	public interface IDataSlicer : INotifyPropertyChanged
+	{
+		string DisplayName { get; set; }
+		List<DataBucket> Slice(IEnumerable<ISliceable> dataSet);
+		ObservableCollection<ISlicerGroup> Groups { get; }
+		ISlicerGroup AddGroup(object value);
+		void RemoveGroup(ISlicerGroup group);
+		void Merge(ISlicerGroup group1, ISlicerGroup group2);
+		void Split(ISlicerGroup group);
+		void SetContext(object context);
+	}
+
+	public interface ISliceable
+	{
+		int GetSliceCount();
+	}
+}
diff --git a/editor source/Desktop/Reporting/DataSlicerControlMap.cs b/editor source/Desktop/Reporting/DataSlicerControlMap.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d96188455e1d5e1780acec71755518a202c04d0a
--- /dev/null
+++ b/editor source/Desktop/Reporting/DataSlicerControlMap.cs	
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace Desktop.Reporting
+{
+	public static class DataSlicerControlMap
+	{
+		private static Dictionary<Type, Type> _map;
+
+		public static Type GetControlType(Type slicerType)
+		{
+			if (_map == null)
+			{
+				_map = new Dictionary<Type, Type>();
+				foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
+				{
+					string name = assembly.FullName.ToLower();
+					if (name.StartsWith("system") ||
+						name.StartsWith("microsoft") ||
+						name.StartsWith("newtonsoft") ||
+						name.StartsWith("mscorlib") ||
+						name.StartsWith("vshost") ||
+						name.StartsWith("windows"))
+					{
+						continue;
+					}
+					foreach (Type type in assembly.GetTypes())
+					{
+						DataSlicerControlAttribute attrib = type.GetCustomAttribute<DataSlicerControlAttribute>();
+						if (attrib != null)
+						{
+							_map[attrib.SlicerType] = type;
+						}
+					}
+				}
+			}
+
+			Type controlType;
+			_map.TryGetValue(slicerType, out controlType);
+			return controlType;
+		}
+	}
+
+	public class DataSlicerControlAttribute : Attribute
+	{
+		public Type SlicerType;
+
+		public DataSlicerControlAttribute(Type slicerType)
+		{
+			SlicerType = slicerType;
+		}
+	}
+
+	public interface ISlicerControl
+	{
+		void SetSlicer(IDataSlicer slicer);
+	}
+}
diff --git a/editor source/Desktop/Reporting/RecordSlicer.cs b/editor source/Desktop/Reporting/RecordSlicer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..91fd0ec482fefc82590a75306f018e25188f18ff
--- /dev/null
+++ b/editor source/Desktop/Reporting/RecordSlicer.cs	
@@ -0,0 +1,116 @@
+using Desktop.CommonControls;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Reflection;
+
+namespace Desktop.Reporting
+{
+	public class RecordSlicer : BaseSlicer<IRecord>
+	{
+		public string Property;
+		public Type RecordType;
+		
+		public RecordSlicer(Type recordType, string property, string displayName, bool includeOther, bool includeNull)
+		{
+			Groups = new ObservableCollection<ISlicerGroup>();
+			if (includeOther)
+			{
+				SlicerGroup<IRecord> catchAll = new SlicerGroup<IRecord>();
+				catchAll.Label = "Other";
+				catchAll.Key = "*";
+				Groups.Add(catchAll);
+			}
+			if (includeNull)
+			{
+				SlicerGroup<IRecord> catchNull = new SlicerGroup<IRecord>();
+				catchNull.Label = "None";
+				catchNull.Key = "-";
+				Groups.Add(catchNull);
+			}
+			Property = property;
+			RecordType = recordType;
+			DisplayName = displayName;
+		}
+
+		public SlicerGroup<IRecord> AddGroup(string key)
+		{
+			IRecord record = RecordLookup.Get(RecordType, key, false, null);
+			if (record != null)
+			{
+				SlicerGroup<IRecord> group = AddGroup(record) as SlicerGroup<IRecord>;
+				Groups.Add(group);
+				return group;
+			}
+			return null;
+		}
+
+		public override List<DataBucket> Slice(IEnumerable<ISliceable> dataSet)
+		{
+			Dictionary<string, DataBucket> buckets = new Dictionary<string, DataBucket>();
+			List<DataBucket> list = new List<DataBucket>();
+			DataBucket catchAll = null;
+			DataBucket catchNull = null;
+			foreach (SlicerGroup<IRecord> group in Groups)
+			{
+				if (!group.Active) { continue; }
+				DataBucket bucket = new DataBucket(group.Label);
+				if (group.Key == "*")
+				{
+					catchAll = bucket;
+					continue;
+				}
+				else if (group.Key == "-")
+				{
+					catchNull = bucket;
+					continue;
+				}
+				foreach (IRecord record in group.Values)
+				{
+					buckets[record.Key] = bucket;
+				}
+				list.Add(bucket);
+			}
+			list.Sort();
+			if (catchAll != null)
+			{
+				//put the catch all at the end
+				list.Add(catchAll);
+			}
+			if (catchNull != null)
+			{
+				list.Add(catchNull);
+			}
+
+			foreach (ISliceable obj in dataSet)
+			{
+				MemberInfo mi = PropertyTypeInfo.GetMemberInfo(obj.GetType(), Property);
+				object value = mi.GetValue(obj);
+				IRecord record = RecordLookup.Get(RecordType, value?.ToString(), false, Context);
+				DataBucket bucket = null;
+				if (record != null)
+				{
+					if (buckets.ContainsKey(record.Key))
+					{
+						bucket = buckets[record.Key];
+					}
+					else if (catchAll != null)
+					{
+						bucket = catchAll;
+					}
+				}
+				else
+				{
+					bucket = catchNull;
+				}
+				if (bucket != null)
+				{
+					bucket.Count += obj.GetSliceCount();
+					bucket.Data.Add(obj);
+				}
+			}
+
+			return list;
+		}
+	}
+}
diff --git a/editor source/Desktop/Reporting/SlicerGroup.cs b/editor source/Desktop/Reporting/SlicerGroup.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2d75e4d909418f9dc12039c372d3fecfb427d2c3
--- /dev/null
+++ b/editor source/Desktop/Reporting/SlicerGroup.cs	
@@ -0,0 +1,67 @@
+using Desktop.DataStructures;
+using System.ComponentModel;
+
+namespace Desktop.Reporting
+{
+	public interface ISlicerGroup : INotifyPropertyChanged
+	{
+		string Key { get; set; }
+		string Label { get; set; }
+		bool Groupable { get; set; }
+		ObservableSet<object> Values { get; }
+		bool Active { get; set; }
+	}
+
+	public class SlicerGroup<T> : BindableObject, ISlicerGroup
+	{
+		public bool Groupable
+		{
+			get { return Get<bool>(); }
+			set { Set(value); }
+		}
+
+		public string Key
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		public string Label
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		public ObservableSet<object> Values
+		{
+			get { return Get<ObservableSet<object>>(); }
+			set { Set(value); }
+		}
+
+		public bool Active
+		{
+			get { return Get<bool>(); }
+			set { Set(value); }
+		}
+
+		public SlicerGroup(object value) : this()
+		{
+			Label = value.ToString();
+			Key = value.ToString();
+			Values.Add(value);
+			Groupable = true;
+		}
+
+		public SlicerGroup()
+		{
+			Values = new ObservableSet<object>();
+			Active = true;
+			Groupable = false;
+		}
+
+		public override string ToString()
+		{
+			return Label;
+		}
+	}
+}
diff --git a/editor source/Desktop/Resources/Add.png b/editor source/Desktop/Resources/Add.png
new file mode 100644
index 0000000000000000000000000000000000000000..bcf90081322b279022e26fae01c9bb0c6bd85c35
--- /dev/null
+++ b/editor source/Desktop/Resources/Add.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:81c01a6aa1e3d41571956640306a3a25952ea0b6949d7f99decb82b290970ff6
+size 190
diff --git a/editor source/Desktop/Resources/Collapse.png b/editor source/Desktop/Resources/Collapse.png
new file mode 100644
index 0000000000000000000000000000000000000000..6e7993d0b4ae64fe722506c0e05a5e29a9882c74
--- /dev/null
+++ b/editor source/Desktop/Resources/Collapse.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:17c2ef485968ff416295e820b59fd02a2b9783b1008d7789a774887e7920e233
+size 271
diff --git a/editor source/Desktop/Resources/DownArrow.png b/editor source/Desktop/Resources/DownArrow.png
new file mode 100644
index 0000000000000000000000000000000000000000..487a4497e3842000cf4b9f5ad1683723f7b20cb0
--- /dev/null
+++ b/editor source/Desktop/Resources/DownArrow.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:312cbabd13962902e380f3d34124169773a549a24383949a166d9f9ebe349c10
+size 237
diff --git a/editor source/Desktop/Resources/Expand.png b/editor source/Desktop/Resources/Expand.png
new file mode 100644
index 0000000000000000000000000000000000000000..aca72fc3c8fe7ac13cecbd84a75e8d3deaacee56
--- /dev/null
+++ b/editor source/Desktop/Resources/Expand.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8ec8c4e8d55ccac9a51c208998f11225a9d533249ac5b16b0af68649629ddfbe
+size 291
diff --git a/editor source/Desktop/Resources/Remove.png b/editor source/Desktop/Resources/Remove.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff8f6a90e2bba8b8aa715062f38b647b559d6721
--- /dev/null
+++ b/editor source/Desktop/Resources/Remove.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1b8fa3ea1fe1617146227ce9ad4a1675321d48747ca2a0b6364ae82745779ca4
+size 229
diff --git a/editor source/Desktop/Resources/UpArrow.png b/editor source/Desktop/Resources/UpArrow.png
new file mode 100644
index 0000000000000000000000000000000000000000..37ccf72044e29cda7cf7d45fcdf78a89f61aedf9
--- /dev/null
+++ b/editor source/Desktop/Resources/UpArrow.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:91da77e99d60eb8078a85910aac41ecf1788c9a5d085f053b02e4bbf30437e8b
+size 236
diff --git a/editor source/Desktop/Serialization/Json.cs b/editor source/Desktop/Serialization/Json.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8464157a71ff37c5ff35835377783e47943ffdb6
--- /dev/null
+++ b/editor source/Desktop/Serialization/Json.cs	
@@ -0,0 +1,32 @@
+using Newtonsoft.Json;
+
+namespace Desktop
+{
+	/// <summary>
+	/// Wrapper around Newtonsoft JSON
+	/// </summary>
+	public static class Json
+	{
+		private static JsonSerializerSettings _settings;
+
+		static Json()
+		{
+			_settings = new JsonSerializerSettings();
+			_settings.TypeNameHandling = TypeNameHandling.Auto;
+			_settings.Formatting = Formatting.Indented;
+			_settings.NullValueHandling = NullValueHandling.Ignore;
+		}
+
+		public static string Serialize(object value)
+		{
+			string result = JsonConvert.SerializeObject(value, _settings);
+			return result;
+		}
+
+		public static T Deserialize<T>(string json)
+		{
+			T obj = JsonConvert.DeserializeObject<T>(json, _settings);
+			return obj;
+		}
+	}
+}
diff --git a/editor source/Desktop/Serialization/PropertyTypeInfo.cs b/editor source/Desktop/Serialization/PropertyTypeInfo.cs
index 866331b1e9b92c14d014c16b7d4902b1c20c4391..58b65714c255edfa6914b970232bfe16c2f93956 100644
--- a/editor source/Desktop/Serialization/PropertyTypeInfo.cs	
+++ b/editor source/Desktop/Serialization/PropertyTypeInfo.cs	
@@ -10,6 +10,7 @@ namespace Desktop
 	{
 		public static DualKeyDictionary<Type, string, Type> _mapping = new DualKeyDictionary<Type, string, Type>();
 		public static DualKeyDictionary<Type, string, FieldInfo> _fieldMapping = new DualKeyDictionary<Type, string, FieldInfo>();
+		public static DualKeyDictionary<Type, string, MemberInfo> _memberMapping = new DualKeyDictionary<Type, string, MemberInfo>();
 
 		public static Type GetType(Type type, string property)
 		{
@@ -37,5 +38,20 @@ namespace Desktop
 			}
 			return fi;
 		}
+
+		public static MemberInfo GetMemberInfo(Type type, string property)
+		{
+			MemberInfo mi = _memberMapping.Get(type, property);
+			if (mi == null)
+			{
+				MemberInfo[] mis = type.GetMember(property, BindingFlags.Public | BindingFlags.Instance);
+				if (mis.Length > 0)
+				{
+					mi = mis[0];
+					_memberMapping.Set(type, property, mi);
+				}
+			}
+			return mi;
+		}
 	}
 }
diff --git a/editor source/Desktop/Shell.cs b/editor source/Desktop/Shell.cs
index 13285b1333a5f511a499eb05cba7477d4a2395dc..c853abad9349f866461687a10e917a15db66382b 100644
--- a/editor source/Desktop/Shell.cs	
+++ b/editor source/Desktop/Shell.cs	
@@ -1,4 +1,5 @@
 using Desktop.Messaging;
+using Desktop.Skinning;
 using System;
 using System.Collections.Generic;
 using System.Drawing;
@@ -7,7 +8,7 @@ using System.Windows.Forms;
 
 namespace Desktop
 {
-	public partial class Shell : Form
+	public partial class Shell : SkinnedForm
 	{
 		private static bool _busy = false;
 
@@ -151,6 +152,58 @@ namespace Desktop
 			AddToolbarSeparator();
 		}
 
+		#region Action bar
+		public ToolStripMenuItem AddActionMenu(Image icon, string tooltip)
+		{
+			ToolStripMenuItem menu = new ToolStripMenuItem();
+			menu.Image = icon;
+			menu.ToolTipText = tooltip;
+			actionStrip.Items.Add(menu);
+			return menu;
+		}
+
+		public ToolStripMenuItem AddActionItem(Image icon, string text, string tooltip, Action action, ToolStripMenuItem submenu)
+		{
+			ToolStripMenuItem menu = new ToolStripMenuItem();
+			menu.Image = icon;
+			if (submenu == null)
+			{
+				menu.ToolTipText = tooltip ?? text;
+				actionStrip.Items.Add(menu);
+			}
+			else
+			{
+				menu.Text = text;
+				menu.ToolTipText = tooltip;
+				submenu.DropDownItems.Add(menu);
+			}
+			menu.Tag = action;
+			menu.Click += Action_Click;
+			return menu;
+		}
+
+		public void AddActionSeparator() { AddActionSeparator(null); }
+		public void AddActionSeparator(ToolStripMenuItem submenu)
+		{
+			ToolStripSeparator separator = new ToolStripSeparator();
+			if (submenu == null)
+			{
+				actionStrip.Items.Add(separator);
+			}
+			else
+			{
+				submenu.DropDownItems.Add(separator);
+			}
+		}
+
+		private void Action_Click(object sender, EventArgs e)
+		{
+			ToolStripMenuItem ctl = sender as ToolStripMenuItem;
+			Action action = ctl.Tag as Action;
+			action?.Invoke();
+		}
+		#endregion
+
 		#region Toolbar
 		public ToolStripMenuItem AddToolbarSubmenu(string caption)
 		{
@@ -263,32 +316,12 @@ namespace Desktop
 		}
 		#endregion
 
-		private void tabWorkspaces_DrawItem(object sender, DrawItemEventArgs e)
+		private void stripActivities_CloseButtonClicked(object sender, EventArgs e)
 		{
-			IWorkspace ws = tabWorkspaces.TabPages[e.Index].Tag as IWorkspace;
+			IWorkspace ws = tabWorkspaces.TabPages[tabWorkspaces.SelectedIndex].Tag as IWorkspace;
 			if (!ws.IsDefault)
 			{
-				e.Graphics.DrawString("x", e.Font, Brushes.Black, e.Bounds.Right - CLOSE_AREA, e.Bounds.Top + 4);
-			}
-			e.Graphics.DrawString(tabWorkspaces.TabPages[e.Index].Text, e.Font, Brushes.Black, e.Bounds.Left + LEADING_SPACE, e.Bounds.Top + 4);
-			e.DrawFocusRectangle();
-		}
-
-		private void tabWorkspaces_MouseClick(object sender, MouseEventArgs e)
-		{
-			//See if an X on a tab was clicked
-			for (int i = 0; i < tabWorkspaces.TabPages.Count; i++)
-			{
-				Rectangle r = tabWorkspaces.GetTabRect(i);
-				Rectangle closeButton = new Rectangle(r.Right - CLOSE_AREA, r.Top, CLOSE_AREA, 20);
-				if (closeButton.Contains(e.Location))
-				{
-					IWorkspace ws = tabWorkspaces.TabPages[i].Tag as IWorkspace;
-					if (!ws.IsDefault)
-					{
-						CloseWorkspace(ws);
-					}
-				}
+				CloseWorkspace(ws);
 			}
 		}
 
@@ -303,7 +336,7 @@ namespace Desktop
 		{
 			bool changingWorkspace = ActiveWorkspace?.Record != (IRecord)record;
 
-			if (!changingWorkspace && ActiveActivity.GetType() == typeof(TActivity))
+			if (!changingWorkspace && ActiveActivity?.GetType() == typeof(TActivity))
 			{
 				//already active, so just pass new run parameters
 				ActiveActivity.UpdateParameters(parameters);
@@ -493,6 +526,7 @@ namespace Desktop
 			page.Text = ws.Caption;
 			page.Controls.Add(ctl);
 			tabWorkspaces.TabPages.Add(page);
+			tabWorkspaces.Visible = true;
 			_workspaces[ws] = ctl;
 			_tabs[ws] = page;
 
@@ -569,6 +603,10 @@ namespace Desktop
 			_tabs.Remove(ws);
 			_workspaces.Remove(ws);
 			tabWorkspaces.Controls.Remove(tab);
+			if (tabWorkspaces.TabPages.Count == 0)
+			{
+				tabWorkspaces.Visible = false;
+			}
 			tab.Dispose();
 		}
 
@@ -679,7 +717,10 @@ namespace Desktop
 				return;
 			IWorkspace ws = e.TabPage.Tag as IWorkspace;
 			if (!Activate(ws.ActiveActivity) || !Activate(ws.ActiveSidebarActivity))
+			{
+				stripActivities.Invalidate(true);
 				e.Cancel = true;
+			}
 		}
 
 		private void Shell_FormClosing(object sender, FormClosingEventArgs e)
@@ -713,15 +754,13 @@ namespace Desktop
 		/// <param name="maximize"></param>
 		public void Maximize(bool maximize)
 		{
-			const int TabHeight = 24;
 			if (maximize)
 			{
 				if (toolbar.Visible)
 				{
 					toolbar.Visible = false;
-					int height = tabWorkspaces.Height;
-					tabWorkspaces.Top = -TabHeight;
-					tabWorkspaces.Height = height + _workspaceTop + TabHeight;
+					actionStrip.Visible = false;
+					stripActivities.Visible = false;
 				}
 			}
 			else
@@ -729,9 +768,8 @@ namespace Desktop
 				if (!toolbar.Visible)
 				{
 					toolbar.Visible = true;
-					int height = tabWorkspaces.Height;
-					tabWorkspaces.Top = _workspaceTop;
-					tabWorkspaces.Height = height - _workspaceTop - TabHeight;
+					actionStrip.Visible = true;
+					stripActivities.Visible = true;
 				}
 			}
 		}
@@ -745,10 +783,11 @@ namespace Desktop
 				FrameUpdate.Invoke(this, EventArgs.Empty);
 		}
 
-		public string ShowOpenFileDialog(string initialDirectory, string filename)
+		public string ShowOpenFileDialog(string initialDirectory, string filename, string filter)
 		{
 			openFileDialog1.InitialDirectory = initialDirectory;
 			openFileDialog1.FileName = filename;
+			openFileDialog1.Filter = filter;
 			if (openFileDialog1.ShowDialog() == DialogResult.OK)
 			{
 				return openFileDialog1.FileName;
diff --git a/editor source/Desktop/Shell.designer.cs b/editor source/Desktop/Shell.designer.cs
index 8a243e8f6b9d62fe25a6372be294c3caa5a0f526..eee5842bda59155913f45937936d98f99f4568dc 100644
--- a/editor source/Desktop/Shell.designer.cs	
+++ b/editor source/Desktop/Shell.designer.cs	
@@ -30,7 +30,7 @@
 		{
 			this.components = new System.ComponentModel.Container();
 			this.toolbar = new System.Windows.Forms.MenuStrip();
-			this.tabWorkspaces = new System.Windows.Forms.TabControl();
+			this.tabWorkspaces = new Desktop.Skinning.SkinnedTabControl();
 			this.folderBrowserDialog1 = new System.Windows.Forms.FolderBrowserDialog();
 			this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog();
 			this.timer1 = new System.Windows.Forms.Timer(this.components);
@@ -38,15 +38,19 @@
 			this.statusStrip1 = new System.Windows.Forms.StatusStrip();
 			this.lblStatus = new System.Windows.Forms.ToolStripStatusLabel();
 			this.tmrAutoTick = new System.Windows.Forms.Timer(this.components);
+			this.stripActivities = new Desktop.Skinning.SkinnedTabStrip();
+			this.actionStrip = new System.Windows.Forms.MenuStrip();
 			this.statusStrip1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// toolbar
 			// 
-			this.toolbar.Location = new System.Drawing.Point(0, 0);
+			this.toolbar.Dock = System.Windows.Forms.DockStyle.None;
+			this.toolbar.Location = new System.Drawing.Point(27, 1);
 			this.toolbar.Name = "toolbar";
-			this.toolbar.Size = new System.Drawing.Size(1304, 24);
+			this.toolbar.Size = new System.Drawing.Size(202, 24);
 			this.toolbar.TabIndex = 0;
+			this.toolbar.Tag = "PrimaryDark";
 			this.toolbar.Text = "menuStrip1";
 			// 
 			// tabWorkspaces
@@ -54,16 +58,15 @@
 			this.tabWorkspaces.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.tabWorkspaces.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed;
-			this.tabWorkspaces.Location = new System.Drawing.Point(0, 27);
+			this.tabWorkspaces.Location = new System.Drawing.Point(1, 55);
+			this.tabWorkspaces.Margin = new System.Windows.Forms.Padding(0);
 			this.tabWorkspaces.Name = "tabWorkspaces";
 			this.tabWorkspaces.Padding = new System.Drawing.Point(21, 3);
 			this.tabWorkspaces.SelectedIndex = 0;
-			this.tabWorkspaces.Size = new System.Drawing.Size(1304, 732);
+			this.tabWorkspaces.Size = new System.Drawing.Size(1302, 706);
 			this.tabWorkspaces.TabIndex = 2;
-			this.tabWorkspaces.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.tabWorkspaces_DrawItem);
+			this.tabWorkspaces.Visible = false;
 			this.tabWorkspaces.Selecting += new System.Windows.Forms.TabControlCancelEventHandler(this.tabWorkspaces_Selecting);
-			this.tabWorkspaces.MouseClick += new System.Windows.Forms.MouseEventHandler(this.tabWorkspaces_MouseClick);
 			// 
 			// timer1
 			// 
@@ -77,11 +80,16 @@
 			// 
 			// statusStrip1
 			// 
+			this.statusStrip1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.statusStrip1.AutoSize = false;
+			this.statusStrip1.Dock = System.Windows.Forms.DockStyle.None;
 			this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.lblStatus});
-			this.statusStrip1.Location = new System.Drawing.Point(0, 762);
+			this.statusStrip1.Location = new System.Drawing.Point(1, 761);
 			this.statusStrip1.Name = "statusStrip1";
-			this.statusStrip1.Size = new System.Drawing.Size(1304, 22);
+			this.statusStrip1.Size = new System.Drawing.Size(1302, 22);
+			this.statusStrip1.SizingGrip = false;
 			this.statusStrip1.TabIndex = 3;
 			this.statusStrip1.Text = "statusStrip1";
 			// 
@@ -95,18 +103,55 @@
 			// 
 			this.tmrAutoTick.Tick += new System.EventHandler(this.tmrAutoTick_Tick);
 			// 
+			// stripActivities
+			// 
+			this.stripActivities.AddCaption = null;
+			this.stripActivities.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.stripActivities.Location = new System.Drawing.Point(1, 25);
+			this.stripActivities.Margin = new System.Windows.Forms.Padding(0);
+			this.stripActivities.Name = "stripActivities";
+			this.stripActivities.PanelType = Desktop.Skinning.SkinnedBackgroundType.Primary;
+			this.stripActivities.ShowAddButton = false;
+			this.stripActivities.ShowCloseButton = true;
+			this.stripActivities.Size = new System.Drawing.Size(1302, 30);
+			this.stripActivities.StartMargin = 103;
+			this.stripActivities.TabControl = this.tabWorkspaces;
+			this.stripActivities.TabIndex = 4;
+			this.stripActivities.TabMargin = 5;
+			this.stripActivities.TabPadding = 20;
+			this.stripActivities.TabSize = 100;
+			this.stripActivities.TabType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.stripActivities.Text = "skinnedTabStrip1";
+			this.stripActivities.Vertical = false;
+			this.stripActivities.CloseButtonClicked += new System.EventHandler(this.stripActivities_CloseButtonClicked);
+			// 
+			// actionStrip
+			// 
+			this.actionStrip.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.actionStrip.Dock = System.Windows.Forms.DockStyle.None;
+			this.actionStrip.Location = new System.Drawing.Point(1009, 1);
+			this.actionStrip.Name = "actionStrip";
+			this.actionStrip.ShowItemToolTips = true;
+			this.actionStrip.Size = new System.Drawing.Size(202, 24);
+			this.actionStrip.TabIndex = 5;
+			this.actionStrip.Tag = "PrimaryDark";
+			// 
 			// Shell
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.BackColor = System.Drawing.SystemColors.Control;
 			this.ClientSize = new System.Drawing.Size(1304, 784);
+			this.Controls.Add(this.actionStrip);
+			this.Controls.Add(this.stripActivities);
 			this.Controls.Add(this.statusStrip1);
 			this.Controls.Add(this.tabWorkspaces);
 			this.Controls.Add(this.toolbar);
 			this.KeyPreview = true;
 			this.MainMenuStrip = this.toolbar;
 			this.Name = "Shell";
+			this.ShowTitleBar = false;
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
 			this.Text = "Shell";
 			this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Shell_FormClosing);
@@ -122,7 +167,7 @@
 		#endregion
 
 		private System.Windows.Forms.MenuStrip toolbar;
-		private System.Windows.Forms.TabControl tabWorkspaces;
+		private Desktop.Skinning.SkinnedTabControl tabWorkspaces;
 		private System.Windows.Forms.FolderBrowserDialog folderBrowserDialog1;
 		private System.Windows.Forms.SaveFileDialog saveFileDialog1;
 		private System.Windows.Forms.Timer timer1;
@@ -130,6 +175,8 @@
 		private System.Windows.Forms.StatusStrip statusStrip1;
 		private System.Windows.Forms.ToolStripStatusLabel lblStatus;
 		private System.Windows.Forms.Timer tmrAutoTick;
+		private Skinning.SkinnedTabStrip stripActivities;
+		private System.Windows.Forms.MenuStrip actionStrip;
 	}
 }
 
diff --git a/editor source/Desktop/Shell.resx b/editor source/Desktop/Shell.resx
index e874cbc60146a9b23352219089801f405cf2394c..51749645dc5cb9099e166844d930f7724adc5a44 100644
--- a/editor source/Desktop/Shell.resx	
+++ b/editor source/Desktop/Shell.resx	
@@ -138,4 +138,7 @@
   <metadata name="tmrAutoTick.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>872, 17</value>
   </metadata>
+  <metadata name="actionStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>990, 17</value>
+  </metadata>
 </root>
\ No newline at end of file
diff --git a/editor source/Desktop/Skinning/Animator.cs b/editor source/Desktop/Skinning/Animator.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a570130fa54bc307459f459f0df8eb992aa82a5f
--- /dev/null
+++ b/editor source/Desktop/Skinning/Animator.cs	
@@ -0,0 +1,51 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class Animator
+	{
+		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly")]
+		public event EventHandler<float> OnUpdate;
+
+		private readonly Timer _timer = new Timer { Interval = 5, Enabled = false };
+
+		private float _progress;
+		public float Value;
+		public float Increment = 0.05f;
+
+		private AnimationDirection _direction;
+
+		public Animator(float increment)
+		{
+			Increment = increment;
+			_timer.Tick += _timer_Tick;
+		}
+
+		public void StartAnimation(AnimationDirection direction)
+		{
+			_direction = direction;
+			_timer.Start();
+		}
+
+		private void _timer_Tick(object sender, EventArgs e)
+		{
+			_progress += (_direction == AnimationDirection.In ? 1 : -1) * Increment;
+			_progress = Math.Min(1, Math.Max(0, _progress));
+			Value = _progress;
+
+			OnUpdate?.Invoke(this, Value);
+			if (_progress == 0 && _direction == AnimationDirection.Out || _progress == 1 && _direction == AnimationDirection.In)
+			{
+				_timer.Stop();
+			}
+		}
+	}
+
+	public enum AnimationDirection
+	{
+		In,
+		Out
+	}
+}
diff --git a/editor source/Desktop/Skinning/ColorSet.cs b/editor source/Desktop/Skinning/ColorSet.cs
new file mode 100644
index 0000000000000000000000000000000000000000..790803a078b9a38acc3c7f6503ca05de965d1c86
--- /dev/null
+++ b/editor source/Desktop/Skinning/ColorSet.cs	
@@ -0,0 +1,151 @@
+using System.Collections.Generic;
+using System.Drawing;
+
+namespace Desktop.Skinning
+{
+	public class ColorSet
+	{
+		public Color Normal = Color.White;
+		public Color Hover = Color.Gray;
+		public Color Pressed = Color.DarkGray;
+		public Color Selected = Color.LightGray;
+		public Color Disabled = Color.Gray;
+		public Color DisabledSelected = Color.DarkGray;
+		public Color ForeColor = Color.Black;
+		public Color DisabledForeColor = Color.DarkGray;
+		public Color Border = Color.LightGray;
+		public Color BorderHover = Color.Gray;
+		public Color BorderSelected = Color.Gray;
+		public Color BorderDisabled = Color.Gray;
+
+		private Dictionary<VisualState, SolidBrush> _brushes = new Dictionary<VisualState, SolidBrush>();
+		private Dictionary<VisualState, Pen> _pens = new Dictionary<VisualState, Pen>();
+		private Dictionary<VisualState, Pen> _borderPens = new Dictionary<VisualState, Pen>();
+
+		public void ClearCache()
+		{
+			_brushes.Clear();
+			_pens.Clear();
+			_borderPens.Clear();
+		}
+
+		private VisualState GetState(VisualState state, bool focused, bool enabled)
+		{
+			switch (state)
+			{
+				case VisualState.Hover:
+					return enabled ? VisualState.Hover : focused ? VisualState.DisabledSelected : VisualState.Disabled;
+				case VisualState.DisabledSelected:
+					return VisualState.DisabledSelected;
+				default:
+					if (focused)
+					{
+						return enabled ? VisualState.Focused : VisualState.DisabledSelected;
+					}
+					return enabled ? state : VisualState.Disabled;
+			}
+		}
+
+		public Color GetColor(VisualState state, bool focused, bool enabled)
+		{
+			state = GetState(state, focused, enabled);
+			return GetColor(state);
+		}
+
+		private Color GetColor(VisualState state)
+		{
+			switch (state)
+			{
+				case VisualState.Hover:
+					return Hover;
+				case VisualState.Pressed:
+					return Pressed;
+				case VisualState.Focused:
+					return Selected;
+				case VisualState.Disabled:
+					return Disabled;
+				case VisualState.DisabledSelected:
+					return DisabledSelected;
+				default:
+					return Normal;
+			}
+		}
+
+		public SolidBrush GetBrush(VisualState state, bool focused, bool enabled)
+		{
+			state = GetState(state, focused, enabled);
+			return GetBrush(state);
+		}
+		public SolidBrush GetBrush(VisualState state)
+		{
+			SolidBrush br;
+			if (!_brushes.TryGetValue(state, out br))
+			{
+				br = new SolidBrush(GetColor(state));
+				_brushes[state] = br;
+			}
+			return br;
+		}
+
+		public Pen GetPen(VisualState state, bool focused, bool enabled)
+		{
+			state = GetState(state, focused, enabled);
+			return GetPen(state);
+		}
+		public Pen GetPen(VisualState state)
+		{
+			Pen pen;
+			if (!_pens.TryGetValue(state, out pen))
+			{
+				pen = new Pen(GetColor(state));
+				_pens[state] = pen;
+			}
+			return pen;
+		}
+
+		private Color GetBorderColor(VisualState state)
+		{
+			switch (state)
+			{
+				case VisualState.Hover:
+					return BorderHover;
+				case VisualState.Disabled:
+					return BorderDisabled;
+				case VisualState.Focused:
+					return BorderSelected;
+				default:
+					return Border;
+			}
+		}
+
+		public Pen GetBorderPen(VisualState state, bool focused, bool enabled)
+		{
+			Pen pen;
+			state = GetState(state, focused, enabled);
+			if (!_borderPens.TryGetValue(state, out pen))
+			{
+				pen = new Pen(GetBorderColor(state));
+				_borderPens[state] = pen;
+			}
+			return pen;
+		}
+
+		public static Color BlendColor(Color c1, Color c2, float a)
+		{
+			float r1 = c1.R / 255.0f;
+			float g1 = c1.G / 255.0f;
+			float b1 = c1.B / 255.0f;
+			float a1 = c1.A / 255.0f;
+			float r2 = c2.R / 255.0f;
+			float g2 = c2.G / 255.0f;
+			float b2 = c2.B / 255.0f;
+			float a2 = c2.A / 255.0f;
+
+			float r = r1 * (1 - a) + r2 * a;
+			float g = g1 * (1 - a) + g2 * a;
+			float b = b1 * (1 - a) + b2 * a;
+			float alpha = a1 * (1 - a) + a2 * a;
+			return Color.FromArgb((byte)(alpha * 255), (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewButtonCell.cs b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewButtonCell.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7fc785af004a1d2be6eababf6859b91091f0b116
--- /dev/null
+++ b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewButtonCell.cs	
@@ -0,0 +1,209 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedDataGridViewButtonCell : DataGridViewButtonCell
+	{
+		private bool _flat = false;
+		public bool Flat
+		{
+			get { return _flat; }
+			set { _flat = value; _colorSet = null; }
+		}
+
+		private SkinnedFieldType _fieldType = SkinnedFieldType.Primary;
+		public SkinnedFieldType FieldType
+		{
+			get { return _fieldType; }
+			set { _fieldType = value; _colorSet = null; }
+		}
+		private ColorSet _colorSet;
+		private Color _foreColor;
+		private bool _mouseInBounds;
+		private ButtonState _buttonState;
+
+		private void GetColorSet()
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			if (Flat)
+			{
+				_colorSet = skin.GetFieldColorSet(FieldType, SkinnedLightLevel.Light);
+				_foreColor = skin.GetForeColor(FieldType);
+			}
+			else
+			{
+				_colorSet = skin.GetFieldColorSet(FieldType, SkinnedLightLevel.Normal);
+				_foreColor = _colorSet.Normal;
+			}
+		}
+
+		private void UpdateButtonState(ButtonState state, int rowIndex)
+		{
+			if (_buttonState != state)
+			{
+				_buttonState = state;
+				DataGridView.InvalidateCell(ColumnIndex, rowIndex);
+			}
+		}
+
+		protected override void OnMouseLeave(int rowIndex)
+		{
+			if (DataGridView == null) { return; }
+
+			if (_mouseInBounds)
+			{
+				_mouseInBounds = false;
+				if (ColumnIndex >= 0 && rowIndex >= 0)
+				{
+					DataGridView.InvalidateCell(ColumnIndex, rowIndex);
+				}
+			}
+			if ((_buttonState & ButtonState.Pushed) != 0)
+			{
+				UpdateButtonState(_buttonState & ~ButtonState.Pushed, rowIndex);
+			}
+			base.OnMouseLeave(rowIndex);
+		}
+
+		protected override void OnMouseMove(DataGridViewCellMouseEventArgs e)
+		{
+			if (DataGridView == null) { return; }
+
+			bool oldInBounds = _mouseInBounds;
+			_mouseInBounds = GetContentBounds(e.RowIndex).Contains(e.X, e.Y);
+			if (oldInBounds != _mouseInBounds)
+			{
+				DataGridView.InvalidateCell(ColumnIndex, e.RowIndex);
+			}
+
+			base.OnMouseMove(e);
+		}
+
+		protected override void OnLeave(int rowIndex, bool throughMouseClick)
+		{
+			if (DataGridView == null) { return; }
+
+			if (_buttonState != ButtonState.Normal)
+			{
+				UpdateButtonState(ButtonState.Normal, rowIndex);
+			}
+
+			base.OnLeave(rowIndex, throughMouseClick);
+		}
+
+		protected override void OnMouseDown(DataGridViewCellMouseEventArgs e)
+		{
+			if (DataGridView == null) { return; }
+
+			if (e.Button == MouseButtons.Left && _mouseInBounds)
+			{
+				UpdateButtonState(_buttonState | ButtonState.Pushed, e.RowIndex);
+			}
+			base.OnMouseDown(e);
+		}
+
+		protected override void OnMouseUp(DataGridViewCellMouseEventArgs e)
+		{
+			if (DataGridView == null) { return; }
+
+			if (e.Button == MouseButtons.Left)
+			{
+				UpdateButtonState(_buttonState & ~ButtonState.Pushed, e.RowIndex);
+			}
+			base.OnMouseUp(e);
+		}
+
+		protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
+		{
+			GetColorSet();
+			VisualState state = VisualState.Normal;
+
+			if ((elementState & DataGridViewElementStates.Selected) != 0)
+			{
+				state = VisualState.Focused;
+			}
+			if (_mouseInBounds)
+			{
+				state = VisualState.Hover;
+			}
+			if (_buttonState == ButtonState.Pushed)
+			{
+				state = VisualState.Pressed;
+			}
+
+			// paint borders and background
+			base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts & ~(DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.ContentForeground));
+
+			Graphics g = graphics;
+			Rectangle contentBounds = new Rectangle(cellBounds.X + 1, cellBounds.Y + 1, cellBounds.Width - 3, cellBounds.Height - 3);
+			if (Flat)
+			{
+				DrawFlat(g, state, contentBounds, formattedValue?.ToString(), cellStyle.BackColor);
+			}
+			else
+			{
+				DrawButton(g, state, contentBounds, formattedValue?.ToString());
+			}
+		}
+
+		private void DrawFlat(Graphics g, VisualState state, Rectangle bounds, string text, Color backColor)
+		{
+			Color foreColor = _foreColor;
+
+			//back
+			if (state == VisualState.Hover)
+			{
+				backColor = _colorSet.Hover;
+				foreColor = _colorSet.ForeColor;
+			}
+			else if (state == VisualState.Pressed)
+			{
+				backColor = _colorSet.Pressed;
+				foreColor = _colorSet.ForeColor;
+			}
+			using (SolidBrush br = new SolidBrush(backColor))
+			{
+				g.FillRectangle(br, bounds);
+			}
+			//text
+			SkinnedButton.DrawContent(g, bounds, foreColor, null, ContentAlignment.MiddleCenter, text, ContentAlignment.MiddleCenter);
+
+			if (state == VisualState.Focused)
+			{
+				SkinManager.Instance.DrawFocusRectangle(g, bounds);
+			}
+		}
+
+		private void DrawButton(Graphics g, VisualState state, Rectangle bounds, string text)
+		{
+			//back
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			SolidBrush backColor = _colorSet.GetBrush(state, false, true);
+			g.FillRectangle(backColor, bounds);
+
+			//text
+			SkinnedButton.DrawContent(g, bounds, _colorSet.ForeColor, null, ContentAlignment.MiddleCenter, text, ContentAlignment.MiddleCenter);
+
+			if (state == VisualState.Focused)
+			{
+				SkinManager.Instance.DrawFocusRectangle(g, bounds);
+			}
+
+			//border
+			Pen borderPen = _colorSet.GetBorderPen(state, false, true);
+			g.DrawRectangle(borderPen, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1);
+		}
+
+		public override object Clone()
+		{
+			SkinnedDataGridViewButtonCell copy = base.Clone() as SkinnedDataGridViewButtonCell;
+			if (copy != null)
+			{
+				copy.Flat = Flat;
+				copy.FieldType = FieldType;
+			}
+			return copy;
+		}
+	}
+}
\ No newline at end of file
diff --git a/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewButtonColumn.cs b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewButtonColumn.cs
new file mode 100644
index 0000000000000000000000000000000000000000..eb71cc3ce9b127d81fb47c648673671699348f92
--- /dev/null
+++ b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewButtonColumn.cs	
@@ -0,0 +1,66 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedDataGridViewButtonColumn : DataGridViewButtonColumn
+	{
+		private SkinnedDataGridViewButtonCell _cellTemplate;
+
+		public bool Flat
+		{
+			get { return _cellTemplate.Flat; }
+			set
+			{
+				_cellTemplate.Flat = value;
+				if (DataGridView != null)
+				{
+					DataGridViewRowCollection rows = DataGridView.Rows;
+					for (int i = 0; i < rows.Count; i++)
+					{
+						DataGridViewRow row = rows.SharedRow(i);
+						SkinnedDataGridViewButtonCell cell = row.Cells[Index] as SkinnedDataGridViewButtonCell;
+						if (cell != null)
+						{
+							cell.Flat = value;
+						}
+					}
+					DataGridView.InvalidateColumn(Index);
+				}
+			}
+		}
+
+		public SkinnedFieldType FieldType
+		{
+			get { return _cellTemplate.FieldType; }
+			set { _cellTemplate.FieldType = value; }
+		}
+
+		public override object Clone()
+		{
+			SkinnedDataGridViewButtonColumn copy = base.Clone() as SkinnedDataGridViewButtonColumn;
+			if (copy != null)
+			{
+				copy.Flat = Flat;
+				copy.FieldType = FieldType;
+			}
+			return copy;
+		}
+
+		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
+		public SkinnedDataGridViewButtonColumn()
+		{
+			CellTemplate = new SkinnedDataGridViewButtonCell();
+		}
+
+		public override DataGridViewCell CellTemplate
+		{
+			get { return base.CellTemplate; }
+			set
+			{
+				base.CellTemplate = value;
+				_cellTemplate = value as SkinnedDataGridViewButtonCell;
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewCheckBoxCell.cs b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewCheckBoxCell.cs
new file mode 100644
index 0000000000000000000000000000000000000000..28ee35b79fb04b779c6f80de0f456a65ae3e28aa
--- /dev/null
+++ b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewCheckBoxCell.cs	
@@ -0,0 +1,126 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedDataGridViewCheckBoxCell : DataGridViewCheckBoxCell, ISkinControl
+	{
+		private bool _mouseInBounds;
+		private ButtonState _buttonState;
+
+		public void OnUpdateSkin(Skin skin)
+		{
+		}
+
+		private void UpdateButtonState(ButtonState state, int rowIndex)
+		{
+			if (_buttonState != state)
+			{
+				_buttonState = state;
+				DataGridView.InvalidateCell(ColumnIndex, rowIndex);
+			}
+		}
+
+		protected override void OnMouseLeave(int rowIndex)
+		{
+			if (DataGridView == null) { return; }
+
+			if (_mouseInBounds)
+			{
+				_mouseInBounds = false;
+				if (ColumnIndex >= 0 && rowIndex >= 0)
+				{
+					DataGridView.InvalidateCell(ColumnIndex, rowIndex);
+				}
+			}
+			if ((_buttonState & ButtonState.Pushed) != 0)
+			{
+				UpdateButtonState(_buttonState & ~ButtonState.Pushed, rowIndex);
+			}
+			base.OnMouseLeave(rowIndex);
+		}
+
+		protected override void OnMouseMove(DataGridViewCellMouseEventArgs e)
+		{
+			if (DataGridView == null) { return; }
+
+			bool oldInBounds = _mouseInBounds;
+			_mouseInBounds = true;
+			if (oldInBounds != _mouseInBounds)
+			{
+				DataGridView.InvalidateCell(ColumnIndex, e.RowIndex);
+			}
+
+			base.OnMouseMove(e);
+		}
+
+		protected override void OnLeave(int rowIndex, bool throughMouseClick)
+		{
+			if (DataGridView == null) { return; }
+
+			if (_buttonState != ButtonState.Normal)
+			{
+				UpdateButtonState(ButtonState.Normal, rowIndex);
+			}
+
+			base.OnLeave(rowIndex, throughMouseClick);
+		}
+
+		protected override void OnMouseDown(DataGridViewCellMouseEventArgs e)
+		{
+			if (DataGridView == null) { return; }
+
+			if (e.Button == MouseButtons.Left && _mouseInBounds)
+			{
+				UpdateButtonState(_buttonState | ButtonState.Pushed, e.RowIndex);
+			}
+			base.OnMouseDown(e);
+		}
+
+		protected override void OnMouseUp(DataGridViewCellMouseEventArgs e)
+		{
+			if (DataGridView == null) { return; }
+			if (e.Button == MouseButtons.Left)
+			{
+				UpdateButtonState(_buttonState & ~ButtonState.Pushed, e.RowIndex);
+			}
+			base.OnMouseUp(e);
+		}
+
+		protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
+		{
+			// paint borders and background
+			base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts & ~(DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.ContentForeground));
+
+			int x = cellBounds.Left + cellBounds.Width / 2 - SkinnedCheckBox.CheckBoxSize / 2 - 1;
+			int y = cellBounds.Top + cellBounds.Height / 2 - SkinnedCheckBox.CheckBoxSize / 2 - 1;
+
+			VisualState state = VisualState.Normal;
+
+			if ((elementState & DataGridViewElementStates.Selected) != 0)
+			{
+				state = VisualState.Focused;
+			}
+			if (_mouseInBounds)
+			{
+				state = VisualState.Hover;
+			}
+			if (_buttonState == ButtonState.Pushed)
+			{
+				state = VisualState.Pressed;
+			}
+
+			CheckState checkState = CheckState.Unchecked;
+			if (formattedValue != null && formattedValue is CheckState)
+			{
+				checkState = (CheckState)formattedValue;
+			}
+			else if (formattedValue != null && formattedValue is bool)
+			{
+				checkState = (bool)formattedValue ? CheckState.Checked : CheckState.Unchecked;
+			}
+
+			SkinnedCheckBox.RenderCheckbox(graphics, x, y, SkinnedFieldType.Primary, checkState, state, DataGridView.Enabled);
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewCheckBoxColumn.cs b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewCheckBoxColumn.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b7ecb4d1b97cc911aff5e30287a279646b811829
--- /dev/null
+++ b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewCheckBoxColumn.cs	
@@ -0,0 +1,14 @@
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedDataGridViewCheckBoxColumn : DataGridViewCheckBoxColumn
+	{
+		private SkinnedDataGridViewCheckBoxCell _cellTemplate;
+
+		public SkinnedDataGridViewCheckBoxColumn()
+		{
+			CellTemplate = _cellTemplate = new SkinnedDataGridViewCheckBoxCell();
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewComboBoxCell.cs b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewComboBoxCell.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ca623abd2d3d965470426eb36fdeb4ecf9add54d
--- /dev/null
+++ b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewComboBoxCell.cs	
@@ -0,0 +1,317 @@
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedDataGridViewComboBoxCell : DataGridViewTextBoxCell, ISkinnedComboBox
+	{
+		private bool _mouseInBounds;
+		private ButtonState _buttonState;
+		private bool _needToShowDropdown;
+		private Timer _showTimer = new Timer();
+
+		public SkinnedDataGridViewComboBoxCell()
+		{
+			Items = new SkinnedComboBox.ObjectCollection(this);
+			_showTimer.Interval = 1;
+			_showTimer.Tick += DataGridView_EditingControlShowing;
+		}
+
+		private bool _autoComplete;
+		[DefaultValue(true)]
+		public bool AutoComplete
+		{
+			get { return _autoComplete; }
+			set
+			{
+				if (value != _autoComplete)
+				{
+					_autoComplete = value;
+				}
+				if (OwnsEditingComboBox(RowIndex))
+				{
+					if (value)
+					{
+						EditingComboBox.AutoCompleteSource = AutoCompleteSource.ListItems;
+						EditingComboBox.AutoCompleteMode = AutoCompleteMode.Append;
+					}
+					else
+					{
+						EditingComboBox.AutoCompleteMode = AutoCompleteMode.None;
+						EditingComboBox.AutoCompleteSource = AutoCompleteSource.None;
+					}
+				}
+			}
+		}
+
+		private SkinnedDataGridViewComboBoxEditingControl EditingComboBox { get; set; }
+
+		public override Type EditType
+		{
+			get
+			{
+				return typeof(SkinnedDataGridViewComboBoxEditingControl);
+			}
+		}
+
+		private SkinnedComboBox.ObjectCollection _items;
+		public SkinnedComboBox.ObjectCollection Items
+		{
+			get { return _items; }
+			set
+			{
+				_items = value;
+				if (OwnsEditingComboBox(RowIndex))
+				{
+					EditingComboBox.Items.Clear();
+					EditingComboBox.Items.AddRange(_items);
+				}
+			}
+		}
+
+		private string _displayMember;
+		public string DisplayMember
+		{
+			get { return _displayMember; }
+			set
+			{
+				_displayMember = value;
+				if (OwnsEditingComboBox(RowIndex))
+				{
+					EditingComboBox.DisplayMember = value;
+				}
+			}
+		}
+
+		private bool OwnsEditingComboBox(int rowIndex)
+		{
+			return rowIndex != -1 && EditingComboBox != null && rowIndex == EditingComboBox.EditingControlRowIndex;
+		}
+
+		public void PreItemChange()
+		{
+
+		}
+
+		public void SortItems()
+		{
+
+		}
+
+		public void PostItemChange()
+		{
+
+		}
+
+		public bool Sorted
+		{
+			get { return _items.Sorted; }
+			set
+			{
+				_items.Sorted = value;
+				if (OwnsEditingComboBox(RowIndex))
+				{
+					EditingComboBox.Sorted = value;
+				}
+			}
+		}
+
+		public override object Clone()
+		{
+			SkinnedDataGridViewComboBoxCell copy = base.Clone() as SkinnedDataGridViewComboBoxCell;
+			if (copy != null)
+			{
+				copy.AutoComplete = AutoComplete;
+				copy.DisplayMember = DisplayMember;
+				copy.Sorted = Sorted;
+				copy.Items.AddRange(Items);
+			}
+			return copy;
+		}
+
+		private void UpdateButtonState(ButtonState state, int rowIndex)
+		{
+			if (_buttonState != state)
+			{
+				_buttonState = state;
+				DataGridView.InvalidateCell(ColumnIndex, rowIndex);
+			}
+		}
+
+		protected override void OnMouseLeave(int rowIndex)
+		{
+			if (DataGridView == null) { return; }
+
+			if (_mouseInBounds)
+			{
+				_mouseInBounds = false;
+				if (ColumnIndex >= 0 && rowIndex >= 0)
+				{
+					DataGridView.InvalidateCell(ColumnIndex, rowIndex);
+				}
+			}
+			if ((_buttonState & ButtonState.Pushed) != 0)
+			{
+				UpdateButtonState(_buttonState & ~ButtonState.Pushed, rowIndex);
+			}
+			base.OnMouseLeave(rowIndex);
+		}
+
+		protected override void OnMouseMove(DataGridViewCellMouseEventArgs e)
+		{
+			if (DataGridView == null) { return; }
+
+			bool oldInBounds = _mouseInBounds;
+			_mouseInBounds = true;
+			if (oldInBounds != _mouseInBounds)
+			{
+				DataGridView.InvalidateCell(ColumnIndex, e.RowIndex);
+			}
+
+			base.OnMouseMove(e);
+		}
+
+		protected override void OnLeave(int rowIndex, bool throughMouseClick)
+		{
+			if (DataGridView == null) { return; }
+
+			if (_buttonState != ButtonState.Normal)
+			{
+				UpdateButtonState(ButtonState.Normal, rowIndex);
+			}
+
+			base.OnLeave(rowIndex, throughMouseClick);
+		}
+
+		protected override void OnMouseDown(DataGridViewCellMouseEventArgs e)
+		{
+			if (DataGridView == null) { return; }
+
+			if (e.Button == MouseButtons.Left && _mouseInBounds)
+			{
+				UpdateButtonState(_buttonState | ButtonState.Pushed, e.RowIndex);
+			}
+			_needToShowDropdown = true;
+			base.OnMouseDown(e);
+		}
+
+		protected override void OnMouseUp(DataGridViewCellMouseEventArgs e)
+		{
+			if (DataGridView == null) { return; }
+			_needToShowDropdown = false;
+			if (e.Button == MouseButtons.Left)
+			{
+				UpdateButtonState(_buttonState & ~ButtonState.Pushed, e.RowIndex);
+			}
+			base.OnMouseUp(e);
+		}
+
+		public override void DetachEditingControl()
+		{
+			if (EditingComboBox != null)
+			{
+				//Remove dropdown event handler, whatever it was
+				EditingComboBox = null;
+			}
+			base.DetachEditingControl();
+		}
+
+		public override Type FormattedValueType
+		{
+			get { return typeof(string); }
+		}
+
+		protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
+		{
+			if (value == null)
+			{
+				return base.GetFormattedValue(value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context);
+			}
+			object displayValue = value;
+			return displayValue;
+		}
+
+		public override object ParseFormattedValue(object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)
+		{
+			if (formattedValue == null)
+			{
+				return base.ParseFormattedValue(formattedValue, cellStyle, formattedValueTypeConverter, valueTypeConverter);
+			}
+			string value = formattedValue.ToString();
+			foreach (object o in Items)
+			{
+				if (o.ToString() == value)
+				{
+					return o;
+				}
+			}
+			return formattedValue;
+		}
+
+		public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
+		{
+			base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
+			SkinnedDataGridViewComboBoxEditingControl comboBox = DataGridView.EditingControl as SkinnedDataGridViewComboBoxEditingControl;
+			if (comboBox != null)
+			{
+				comboBox.DropDownStyle = ComboBoxStyle.DropDownList;
+				comboBox.Items.Clear();
+				comboBox.Items.AddRange(Items);
+				comboBox.Sorted = Sorted;
+				comboBox.FieldType = SkinnedFieldType.Surface;
+				comboBox.SelectedItem = initialFormattedValue;
+				comboBox.DisplayMember = DisplayMember;
+				//attach to dropdown event for resizing?
+
+				EditingComboBox = comboBox;
+				Rectangle rectBottomSection = DataGridView.GetCellDisplayRectangle(ColumnIndex, rowIndex, true);
+				rectBottomSection.Y += 21;
+				rectBottomSection.Height -= 21;
+				DataGridView.Invalidate(rectBottomSection);
+
+				if (_needToShowDropdown)
+				{
+					_showTimer.Start();
+				}
+			}
+		}
+
+		private void DataGridView_EditingControlShowing(object sender, EventArgs e)
+		{
+			_showTimer.Stop();
+			EditingComboBox.ShowDropDown();
+			_needToShowDropdown = false;
+		}
+
+		protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
+		{
+			// paint borders and background
+			base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts & ~(DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.ContentForeground));
+
+			Graphics g = graphics;
+			Rectangle contentBounds = new Rectangle(cellBounds.X + 1, cellBounds.Y, cellBounds.Width - 2, cellBounds.Height - 1);
+
+			bool focused = (cellState & DataGridViewElementStates.Selected) != 0;
+
+			VisualState state = VisualState.Normal;
+
+			if ((cellState & DataGridViewElementStates.Selected) != 0)
+			{
+				state = VisualState.Focused;
+			}
+			if (_mouseInBounds)
+			{
+				state = VisualState.Hover;
+			}
+			if (_buttonState == ButtonState.Pushed)
+			{
+				state = VisualState.Pressed;
+			}
+
+			string formatted = SkinnedComboBox.GetFormattedValue(formattedValue, DisplayMember);
+			SkinnedComboBox.RenderComboBox(g, contentBounds, formatted, false, focused, DataGridView.Enabled, ComboBoxStyle.DropDownList, SkinnedFieldType.Surface, state);
+		}
+	}
+}
\ No newline at end of file
diff --git a/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewComboBoxColumn.cs b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewComboBoxColumn.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1b7c3941439bb549d59dd00d7e65acf19db93ebb
--- /dev/null
+++ b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewComboBoxColumn.cs	
@@ -0,0 +1,49 @@
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedDataGridViewComboBoxColumn : DataGridViewColumn
+	{
+		private SkinnedDataGridViewComboBoxCell _cellTemplate;
+
+		public SkinnedDataGridViewComboBoxColumn()
+		{
+			CellTemplate = _cellTemplate = new SkinnedDataGridViewComboBoxCell();
+		}
+
+		public bool AutoComplete
+		{
+			get { return _cellTemplate.AutoComplete; }
+			set { _cellTemplate.AutoComplete = value; }
+		}
+
+		public bool Sorted
+		{
+			get { return _cellTemplate.Sorted; }
+			set { _cellTemplate.Sorted = value; }
+		}
+
+		public string DisplayMember
+		{
+			get { return _cellTemplate.DisplayMember; }
+			set { _cellTemplate.DisplayMember = value; }
+		}
+
+		public SkinnedComboBox.ObjectCollection Items
+		{
+			get { return _cellTemplate.Items; }
+			set { _cellTemplate.Items = value; }
+		}
+
+		public override object Clone()
+		{
+			SkinnedDataGridViewComboBoxColumn copy = base.Clone() as SkinnedDataGridViewComboBoxColumn;
+			copy.AutoComplete = AutoComplete;
+			copy.Sorted = Sorted;
+			copy.DisplayMember = DisplayMember;
+			Items = copy.Items;
+			return copy;
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewComboBoxEditingControl.cs b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewComboBoxEditingControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5ca7fa05caa38e3ad42930515135a19ed609239a
--- /dev/null
+++ b/editor source/Desktop/Skinning/DataGridView/SkinnedDataGridViewComboBoxEditingControl.cs	
@@ -0,0 +1,148 @@
+using System;
+using System.Drawing;
+using System.Globalization;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedDataGridViewComboBoxEditingControl : SkinnedComboBox, IDataGridViewEditingControl
+	{
+		private DataGridView _dataGridView;
+		private bool _valueChanged;
+		private int _rowIndex;
+
+		public SkinnedDataGridViewComboBoxEditingControl() : base()
+		{
+			DropDownStyle = ComboBoxStyle.DropDownList;
+			TabStop = false;
+		}
+
+		public virtual DataGridView EditingControlDataGridView
+		{
+			get
+			{
+				return _dataGridView;
+			}
+			set
+			{
+				_dataGridView = value;
+			}
+		}
+
+		public virtual object EditingControlFormattedValue
+		{
+			get
+			{
+				return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting);
+			}
+			set
+			{
+				string valueStr = value as string;
+				if (valueStr != null)
+				{
+					Text = valueStr;
+					if (string.Compare(valueStr, this.Text, true, CultureInfo.CurrentCulture) != 0)
+					{
+						SelectedIndex = -1;
+					}
+				}
+			}
+		}
+
+		public virtual int EditingControlRowIndex
+		{
+			get
+			{
+				return _rowIndex;
+			}
+			set
+			{
+				_rowIndex = value;
+			}
+		}
+
+		public virtual bool EditingControlValueChanged
+		{
+			get
+			{
+				return _valueChanged;
+			}
+			set
+			{
+				_valueChanged = value;
+			}
+		}
+
+		public virtual Cursor EditingPanelCursor
+		{
+			get
+			{
+				return Cursors.Default;
+			}
+		}
+
+		public virtual bool RepositionEditingControlOnValueChange
+		{
+			get
+			{
+				return false;
+			}
+		}
+
+		public virtual void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
+		{
+			this.Font = dataGridViewCellStyle.Font;
+			if (dataGridViewCellStyle.BackColor.A < 255)
+			{
+				// Our ComboBox does not support transparent back colors
+				Color opaqueBackColor = Color.FromArgb(255, dataGridViewCellStyle.BackColor);
+				BackColor = opaqueBackColor;
+				_dataGridView.EditingPanel.BackColor = opaqueBackColor;
+			}
+			else
+			{
+				BackColor = dataGridViewCellStyle.BackColor;
+			}
+			ForeColor = dataGridViewCellStyle.ForeColor;
+		}
+
+		public virtual bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey)
+		{
+			if ((keyData & Keys.KeyCode) == Keys.Down ||
+				(keyData & Keys.KeyCode) == Keys.Up ||
+				(DroppedDown && ((keyData & Keys.KeyCode) == Keys.Escape) || (keyData & Keys.KeyCode) == Keys.Enter))
+			{
+				return true;
+			}
+			return !dataGridViewWantsInputKey;
+		}
+
+		public virtual object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
+		{
+			return Text;
+		}
+
+		public virtual void PrepareEditingControlForEdit(bool selectAll)
+		{
+			if (selectAll)
+			{
+				SelectAll();
+			}
+		}
+
+		private void NotifyDataGridViewOfValueChange()
+		{
+			_valueChanged = true;
+			_dataGridView.NotifyCurrentCellDirty(true);
+		}
+
+		protected override void OnSelectedIndexChanged(EventArgs e)
+		{
+			base.OnSelectedIndexChanged(e);
+			if (SelectedIndex != -1)
+			{
+				NotifyDataGridViewOfValueChange();
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/ISkinControl.cs b/editor source/Desktop/Skinning/ISkinControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7f31cab494d62159722598474420ba5fc3a950ca
--- /dev/null
+++ b/editor source/Desktop/Skinning/ISkinControl.cs	
@@ -0,0 +1,7 @@
+namespace Desktop.Skinning
+{
+	public interface ISkinControl
+	{
+		void OnUpdateSkin(Skin skin);
+	}
+}
diff --git a/editor source/Desktop/Skinning/Skin.cs b/editor source/Desktop/Skinning/Skin.cs
new file mode 100644
index 0000000000000000000000000000000000000000..13ea761c2d981344f258795b4ac7efead809d17c
--- /dev/null
+++ b/editor source/Desktop/Skinning/Skin.cs	
@@ -0,0 +1,422 @@
+using Desktop.CommonControls.PropertyControls;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+
+namespace Desktop.Skinning
+{
+	public class Skin : IComparable<Skin>, IRecord
+	{
+		public static Font HeaderFont = new Font("Segoe UI", 12);
+		public static Font TextFont = new Font("Microsoft Sans Serif", 8.25f);
+		public static Font ButtonFont = new Font("Microsoft Sans Serif", 8.25f, FontStyle.Bold);
+		public static Font TabFont = new Font("Segoe UI", 9);
+		public static Font ActiveTabFont = new Font("Segoe UI", 9, FontStyle.Bold);
+		public static Font TitleFont = new Font("Segoe UI", 16);
+		public static Font CompletionFont = new Font("Segoe UI", 28);
+
+		[Text(DisplayName = "Name")]
+		public string Name { get; set; }
+		[JsonIgnore]
+		public string Key { get { return Name; } set { Name = value; } }
+		[Text(DisplayName = "Group")]
+		public string Group { get; set; }
+		[Text(DisplayName = "Description")]
+		public string Description { get; set; }
+
+		[ColorSet(DisplayName = "Primary")]
+		public ColorSet PrimaryColor { get; set; } = new ColorSet();
+		[ColorSet(DisplayName = "Light 1")]
+		public ColorSet PrimaryLightColor { get; set; } = new ColorSet();
+		[ColorSet(DisplayName = "Dark 1")]
+		public ColorSet PrimaryDarkColor { get; set; } = new ColorSet();
+
+		[ColorSet(DisplayName = "Secondary")]
+		public ColorSet SecondaryColor { get; set; } = new ColorSet();
+		[ColorSet(DisplayName = "Light 2")]
+		public ColorSet SecondaryLightColor { get; set; } = new ColorSet();
+		[ColorSet(DisplayName = "Dark 2")]
+		public ColorSet SecondaryDarkColor { get; set; } = new ColorSet();
+
+		[ColorSet(DisplayName = "Surface")]
+		public ColorSet Surface { get; set; } = new ColorSet();
+
+		[ColorSet(DisplayName = "Background")]
+		public ColorSet Background { get; set; } = new ColorSet();
+
+		[ColorSet(DisplayName = "Widget")]
+		public ColorSet PrimaryWidget { get; set; } = new ColorSet();
+
+		[ColorSet(DisplayName = "SecondaryWidget")]
+		public ColorSet SecondaryWidget { get; set; } = new ColorSet();
+
+		[Color(DisplayName = "Shadow")]
+		public Color SurfaceShadowColor = Color.LightGray;
+
+		[Color(DisplayName = "Field Back")]
+		public Color FieldBackColor = Color.White;
+		[Color(DisplayName = "Field Alt")]
+		public Color FieldAltBackColor = SystemColors.ControlLightLight;
+		[Color(DisplayName = "Field Disabled")]
+		public Color FieldDisabledBackColor = SystemColors.ControlLight;
+
+		[Color(DisplayName = "Label Text")]
+		public Color LabelForeColor = Color.FromArgb(127, 0, 0, 0);
+		[Color(DisplayName = "Primary Text")]
+		public Color PrimaryForeColor = Color.Blue;
+		[Color(DisplayName = "Primary Text (Light)")]
+		public Color PrimaryLightForeColor = Color.Blue;
+		[Color(DisplayName = "Secondary Text")]
+		public Color SecondaryForeColor = Color.Red;
+		[Color(DisplayName = "Secondary Text (Light)")]
+		public Color SecondaryLightForeColor = Color.Red;
+
+		[Color(DisplayName = "Separator")]
+		public Color Separator = Color.FromArgb(220, 220, 220);
+		[Color(DisplayName = "Hover Effect")]
+		public Color HoverOverlay = Color.FromArgb(50, 127, 127, 127);
+		[Color(DisplayName = "Press Effect")]
+		public Color PressOverlay = Color.FromArgb(50, 64, 64, 64);
+
+		[Color(DisplayName = "Error")]
+		public Color ErrorBackColor = Color.DarkRed;
+
+		[Color(DisplayName = "Good")]
+		public Color GoodForeColor = Color.Green;
+
+		[Color(DisplayName = "Bad")]
+		public Color BadForeColor = Color.Red;
+
+		[Color(DisplayName = "Focus Rect")]
+		public Color FocusRectangle = Color.Black;
+
+		#region Colors visible on a field
+		[Color(DisplayName = "Gray")]
+		public Color Gray = Color.Gray;
+		[Color(DisplayName = "Light Gray")]
+		public Color LightGray = Color.LightGray;
+		[Color(DisplayName = "Orange")]
+		public Color Orange = Color.OrangeRed;
+		[Color(DisplayName = "Green")]
+		public Color Green = Color.Green;
+		[Color(DisplayName = "Blue")]
+		public Color Blue = Color.Blue;
+		[Color(DisplayName = "Purple")]
+		public Color Purple = Color.Purple;
+		[Color(DisplayName = "Pink")]
+		public Color Pink = Color.Pink;
+		[Color(DisplayName = "Red")]
+		public Color Red = Color.Red;
+		#endregion
+
+		#region Accordion Groupers
+		[Color(DisplayName = "Group 1")]
+		public Color Group1 = Color.FromArgb(148, 89, 160);
+		[Color(DisplayName = "Group 2")]
+		public Color Group2 = Color.FromArgb(0, 98, 173);
+		[Color(DisplayName = "Group 3")]
+		public Color Group3 = Color.FromArgb(86, 119, 41);
+		[Color(DisplayName = "Group 4")]
+		public Color Group4 = Color.FromArgb(175, 89, 49);
+		[Color(DisplayName = "Group 5")]
+		public Color Group5 = Color.Black;
+
+		public Color GetGrouper(int number)
+		{
+			switch (number)
+			{
+				case 1:
+					return Group1;
+				case 2:
+					return Group2;
+				case 3:
+					return Group3;
+				case 4:
+					return Group4;
+				case 5:
+					return Group5;
+				default:
+					return Surface.ForeColor;
+			}
+		}
+		#endregion
+
+		#region App-specific colors
+		public Dictionary<string, Color> AppColors = new Dictionary<string, Color>();
+		#endregion
+
+		public Skin()
+		{
+			ApplyDefaults();
+		}
+
+		public override string ToString()
+		{
+			return Name;
+		}
+
+		private void ApplyDefaults()
+		{
+			Name = "Default";
+			Background = new ColorSet();
+			PrimaryColor = new ColorSet()
+			{
+				Normal = Color.FromArgb(92, 107, 192),
+				Hover = Color.FromArgb(117, 132, 217),
+				Pressed = Color.FromArgb(67, 82, 167),
+				Selected = Color.FromArgb(92, 107, 192),
+				Disabled = Color.Gray,
+				ForeColor = Color.White,
+				DisabledForeColor = Color.FromArgb(191, 191, 191),
+			};
+			PrimaryDarkColor = new ColorSet()
+			{
+				Normal = Color.FromArgb(38, 65, 143),
+				Hover = Color.FromArgb(63, 90, 168),
+				Pressed = Color.FromArgb(13, 40, 118),
+				Selected = Color.FromArgb(38, 65, 143),
+				Disabled = Color.Gray,
+				ForeColor = Color.White,
+				DisabledForeColor = Color.FromArgb(191, 191, 191),
+			};
+			PrimaryLightColor = new ColorSet()
+			{
+				Normal = Color.FromArgb(142, 153, 243),
+				Hover = Color.FromArgb(117, 128, 218),
+				Pressed = Color.FromArgb(167, 178, 255),
+				Selected = Color.FromArgb(142, 153, 243),
+				Disabled = Color.FromArgb(205, 205, 205),
+				ForeColor = Color.Black,
+				DisabledForeColor = Color.FromArgb(191, 191, 191),
+			};
+			SecondaryColor = new ColorSet()
+			{
+				Normal = Color.White,
+				Hover = Color.Gray,
+				Pressed = Color.DarkGray,
+				Selected = Color.White,
+				Disabled = Color.Gray,
+				ForeColor = Color.Black,
+				DisabledForeColor = Color.Gray
+			};
+
+			FieldBackColor = Color.White;
+			FieldDisabledBackColor = SystemColors.ControlLightLight;
+		}
+
+		public Color GetAppColor(string name)
+		{
+			Color output = Color.Black;
+			AppColors.TryGetValue(name, out output);
+			return output;
+		}
+
+		/// <summary>
+		/// Creates an icon representing this theme
+		/// </summary>
+		/// <returns></returns>
+		public Bitmap GetIcon()
+		{
+			Bitmap bmp = new Bitmap(16, 16);
+			Graphics g = Graphics.FromImage(bmp);
+			g.DrawRectangle(PrimaryColor.GetBorderPen(VisualState.Normal, false, true), 0, 0, bmp.Width - 1, bmp.Height - 1);
+			//main color on top
+			g.FillRectangle(PrimaryColor.GetBrush(VisualState.Normal, false, true), 1, 1, bmp.Width - 2, 6);
+			//secondary in middle
+			g.FillRectangle(SecondaryColor.GetBrush(VisualState.Normal, false, true), 1, 7, bmp.Width - 2, 3);
+			//background on bottom
+			g.FillRectangle(Background.GetBrush(VisualState.Normal, false, true), 1, 10, bmp.Width - 2, 5);
+
+			g.Dispose();
+			return bmp;
+		}
+
+		public int CompareTo(Skin other)
+		{
+			return Name.CompareTo(other.Name);
+		}
+
+		public int CompareTo(IRecord other)
+		{
+			return Name.CompareTo(other.Name);
+		}
+
+		public Color GetForeColor(SkinnedFieldType type)
+		{
+			switch (type)
+			{
+
+				case SkinnedFieldType.Primary:
+					return PrimaryForeColor;
+				case SkinnedFieldType.Secondary:
+					return SecondaryForeColor;
+				default:
+					return Surface.ForeColor;
+			}
+		}
+
+		public ColorSet GetFieldColorSet(SkinnedFieldType type, SkinnedLightLevel level)
+		{
+			switch (type)
+			{
+				case SkinnedFieldType.Primary:
+					switch (level)
+					{
+						case SkinnedLightLevel.Dark:
+							return PrimaryDarkColor;
+						case SkinnedLightLevel.Light:
+							return PrimaryLightColor;
+						default:
+							return PrimaryColor;
+					}
+				case SkinnedFieldType.Secondary:
+					switch (level)
+					{
+						case SkinnedLightLevel.Dark:
+							return SecondaryDarkColor;
+						case SkinnedLightLevel.Light:
+							return SecondaryLightColor;
+						default:
+							return SecondaryColor;
+					}
+				default:
+					return Surface;
+			}
+		}
+
+		public ColorSet GetWidgetColorSet(SkinnedFieldType type)
+		{
+			switch (type)
+			{
+				case SkinnedFieldType.Secondary:
+					return SecondaryWidget;
+				default:
+					return PrimaryWidget;
+			}
+		}
+
+		public ColorSet GetColorSet(SkinnedBackgroundType type)
+		{
+			switch (type)
+			{
+				case SkinnedBackgroundType.Surface:
+					return Surface;
+				case SkinnedBackgroundType.Primary:
+					return PrimaryColor;
+				case SkinnedBackgroundType.PrimaryDark:
+					return PrimaryDarkColor;
+				case SkinnedBackgroundType.PrimaryLight:
+					return PrimaryLightColor;
+				case SkinnedBackgroundType.Secondary:
+					return SecondaryColor;
+				case SkinnedBackgroundType.SecondaryDark:
+					return SecondaryDarkColor;
+				case SkinnedBackgroundType.SecondaryLight:
+					return SecondaryLightColor;
+				default:
+					return Background;
+			}
+		}
+
+		public Color GetBackColor(SkinnedBackgroundType type)
+		{
+			switch (type)
+			{
+				case SkinnedBackgroundType.Surface:
+					return Surface.Normal;
+				case SkinnedBackgroundType.Primary:
+					return PrimaryColor.Normal;
+				case SkinnedBackgroundType.PrimaryDark:
+					return PrimaryDarkColor.Normal;
+				case SkinnedBackgroundType.PrimaryLight:
+					return PrimaryLightColor.Normal;
+				case SkinnedBackgroundType.Secondary:
+					return SecondaryColor.Normal;
+				case SkinnedBackgroundType.SecondaryDark:
+					return SecondaryDarkColor.Normal;
+				case SkinnedBackgroundType.SecondaryLight:
+					return SecondaryLightColor.Normal;
+				case SkinnedBackgroundType.Transparent:
+					return Color.Transparent;
+				case SkinnedBackgroundType.Field:
+					return FieldBackColor;
+				default:
+					return Background.Normal;
+			}
+		}
+
+		public Color GetForeColor(SkinnedBackgroundType type)
+		{
+			switch (type)
+			{
+				case SkinnedBackgroundType.Surface:
+					return Surface.ForeColor;
+				case SkinnedBackgroundType.Primary:
+					return PrimaryColor.ForeColor;
+				case SkinnedBackgroundType.PrimaryDark:
+					return PrimaryDarkColor.ForeColor;
+				case SkinnedBackgroundType.PrimaryLight:
+					return PrimaryLightColor.ForeColor;
+				case SkinnedBackgroundType.Secondary:
+					return SecondaryColor.ForeColor;
+				case SkinnedBackgroundType.SecondaryDark:
+					return SecondaryDarkColor.ForeColor;
+				case SkinnedBackgroundType.SecondaryLight:
+					return SecondaryLightColor.ForeColor;
+				case SkinnedBackgroundType.Background:
+					return Background.ForeColor;
+				default:
+					return Surface.ForeColor;
+			}
+		}
+
+		public string ToLookupString()
+		{
+			throw new NotImplementedException();
+		}
+	}
+
+	/// <summary>
+	/// Which color set to use for a control's background
+	/// </summary>
+	public enum SkinnedBackgroundType
+	{
+		Background,
+		Surface,
+		PrimaryDark,
+		Primary,
+		PrimaryLight,
+		SecondaryDark,
+		Secondary,
+		SecondaryLight,
+		Transparent,
+		Field,
+	}
+
+	/// <summary>
+	/// Which color set to use for a field
+	/// </summary>
+	public enum SkinnedFieldType
+	{
+		Surface,
+		Primary,
+		Secondary
+	}
+
+	public enum SkinnedLightLevel
+	{
+		Normal,
+		Light,
+		Dark
+	}
+
+	public enum SkinnedHighlight
+	{
+		Normal,
+		Label,
+		Good,
+		Bad,
+		Heading,
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinManager.cs b/editor source/Desktop/Skinning/SkinManager.cs
new file mode 100644
index 0000000000000000000000000000000000000000..546d69d168f06eda3c2c42ca31577d6dac615905
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinManager.cs	
@@ -0,0 +1,241 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public sealed class SkinManager : IDisposable
+	{
+		private static SkinManager _instance;
+		public static SkinManager Instance
+		{
+			get
+			{
+				if (_instance == null)
+				{
+					_instance = new SkinManager();
+				}
+				return _instance;
+			}
+		}
+
+		public List<Skin> AvailableSkins { get; private set; } = new List<Skin>();
+		public Skin CurrentSkin { get; private set; } = new Skin();
+
+		public Pen FocusRectangle = new Pen(Color.Black) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dot };
+
+		//Controls that are registered to get a color from an ISkinnedPanel ancestor
+		//Because the right color could change anytime the control is moved into another hierarchy, we need to track all ancestors and run the event
+		//whenever one of their parents changes
+		private Dictionary<ISkinControl, HashSet<Control>> _hierarchyListeners = new Dictionary<ISkinControl, HashSet<Control>>();
+		private Dictionary<ISkinControl, EventHandler> _hierarchyHandlers = new Dictionary<ISkinControl, EventHandler>();
+		private HashSet<ISkinControl> _pendingUpdates = new HashSet<ISkinControl>();
+		private Timer _timer = new Timer();
+
+		private SkinManager()
+		{
+			_timer.Interval = 1;
+			_timer.Tick += DispatchPendingUpdates;
+		}
+
+		public void Dispose()
+		{
+			Dispose(true);
+			GC.SuppressFinalize(this);
+		}
+		private void Dispose(bool disposing)
+		{
+			if (disposing)
+			{
+				_timer.Dispose();
+			}
+		}
+
+		public void LoadSkins(string path)
+		{
+			Skin defaultSkin = new Skin();
+
+			if (!Directory.Exists(path))
+			{
+				Directory.CreateDirectory(path);
+			}
+			foreach (string file in Directory.EnumerateFiles(path, "*.skin"))
+			{
+				try
+				{
+					string json = File.ReadAllText(file);
+					Skin skin = Json.Deserialize<Skin>(json);
+					bool replaced = false;
+					for (int i = 0; i < AvailableSkins.Count; i++)
+					{
+						if (AvailableSkins[i].Name == skin.Name)
+						{
+							AvailableSkins[i] = skin;
+							replaced = true;
+							break;
+						}
+					}
+					if (!replaced)
+					{
+						AvailableSkins.Add(skin);
+					}
+					if (skin.Name == defaultSkin.Name)
+					{
+						defaultSkin = skin;
+					}
+				}
+				catch { }
+			}
+
+			if (AvailableSkins.Count == 0)
+			{
+				AvailableSkins.Add(defaultSkin);
+			}
+
+			AvailableSkins.Sort();
+			CurrentSkin = defaultSkin;
+		}
+
+		public void SetSkin(Skin skin)
+		{
+			CurrentSkin = skin;
+			FocusRectangle.Color = skin.FocusRectangle;
+			Shell.Instance.PostOffice.SendMessage(CoreDesktopMessages.SkinChanged, CurrentSkin);
+		}
+
+		public void DrawFocusRectangle(Graphics g, Rectangle clientRectangle)
+		{
+			g.DrawRectangle(FocusRectangle, clientRectangle.X + 2, clientRectangle.Y + 2, clientRectangle.Width - 5, clientRectangle.Height - 5);
+		}
+
+		public void ReskinControl(Control ctl, Skin skin)
+		{
+			UpdateSkin(ctl, skin);
+			foreach (Control child in ctl.Controls)
+			{
+				UpdateSkin(child, skin);
+			}
+		}
+
+		public static void UpdateSkin(Control ctl, Skin skin)
+		{
+			ISkinControl skinnedCtl = ctl as ISkinControl;
+			ctl.Invalidate(true);
+			if (skinnedCtl != null)
+			{
+				skinnedCtl.OnUpdateSkin(skin);
+			}
+			else if (ctl is ToolStrip)
+			{
+				ToolStrip ts = ctl as ToolStrip;
+				ts.Font = Skin.TextFont;
+				ts.Renderer = new SkinnedToolStripRenderer(skin, ts);
+
+				foreach (ToolStripItem item in ts.Items)
+				{
+					ToolStripMenuItem mi = item as ToolStripMenuItem;
+					if (mi != null)
+					{
+						ToolStripDropDown dropdown = mi.DropDown;
+						if (dropdown != null)
+						{
+							Instance.ReskinControl(dropdown, skin);
+						}
+					}
+				}
+			}
+			foreach (Control child in ctl.Controls)
+			{
+				UpdateSkin(child, skin);
+			}
+		}
+
+		#region Hierarchical management
+		public void RegisterControl(ISkinControl listener)
+		{
+			if (!_hierarchyListeners.ContainsKey(listener))
+			{
+				Control ctl = listener as Control;
+				if (ctl != null)
+				{
+					ctl.HandleDestroyed += Ctl_HandleDestroyed;
+				}
+				ReplaceListeners(listener);
+			}
+		}
+
+		private void Ctl_HandleDestroyed(object sender, EventArgs e)
+		{
+			UnregisterControl(sender as ISkinControl);
+		}
+
+		private void ReplaceListeners(ISkinControl listener)
+		{
+			HashSet<Control> set;
+			EventHandler handler;
+			UnregisterControl(listener);
+			handler = new EventHandler((object sender, EventArgs e) =>
+			{
+				EnqueueHierarchyChangeEvent(listener);
+			});
+			_hierarchyHandlers[listener] = handler;
+			set = new HashSet<Control>();
+			_hierarchyListeners[listener] = set;
+			Control ctl = listener as Control;
+			while (ctl != null)
+			{
+				ctl.ParentChanged += handler;
+				set.Add(ctl);
+				ctl = ctl.Parent;
+			}
+		}
+
+		public void UnregisterControl(ISkinControl listener)
+		{
+			HashSet<Control> set;
+			EventHandler handler;
+			if (_hierarchyHandlers.TryGetValue(listener, out handler))
+			{
+				if (_hierarchyListeners.TryGetValue(listener, out set))
+				{
+					foreach (Control c in set)
+					{
+						c.ParentChanged -= handler;
+					}
+					set.Clear();
+				}
+				_hierarchyHandlers.Remove(listener);
+			}
+		}
+
+		private void EnqueueHierarchyChangeEvent(ISkinControl listener)
+		{
+			Control ctl = listener as Control;
+			if (ctl != null && ctl.IsDisposed)
+			{
+				return;
+			}
+			_pendingUpdates.Add(listener);
+			_timer.Start();
+		}
+
+		private void DispatchPendingUpdates(object sender, EventArgs e)
+		{
+			Skin skin = CurrentSkin;
+			_timer.Stop();
+			foreach (ISkinControl listener in _pendingUpdates)
+			{
+				Control ctl = listener as Control;
+				if (ctl != null && ctl.IsDisposed)
+				{
+					continue;
+				}
+				listener.OnUpdateSkin(skin);
+			}
+			_pendingUpdates.Clear();
+		}
+		#endregion
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinTester.Designer.cs b/editor source/Desktop/Skinning/SkinTester.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..66571010296b2603204e2aa9bcb3a3598e14e5c4
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinTester.Designer.cs	
@@ -0,0 +1,1641 @@
+namespace Desktop.Skinning
+{
+	partial class SkinTester
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SkinTester));
+			System.Windows.Forms.ListViewGroup listViewGroup1 = new System.Windows.Forms.ListViewGroup("Group 1", System.Windows.Forms.HorizontalAlignment.Left);
+			System.Windows.Forms.ListViewGroup listViewGroup2 = new System.Windows.Forms.ListViewGroup("Group 2", System.Windows.Forms.HorizontalAlignment.Left);
+			System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem(new string[] {
+            "Item 1",
+            "Detail 1"}, -1);
+			System.Windows.Forms.ListViewItem listViewItem2 = new System.Windows.Forms.ListViewItem(new string[] {
+            "Item 2",
+            "Detail 2"}, -1);
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.menuButtonToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.menuItemToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.menuItemToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
+			this.menuButtonToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
+			this.menuStrip1 = new System.Windows.Forms.MenuStrip();
+			this.statusStrip1 = new System.Windows.Forms.StatusStrip();
+			this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
+			this.panel1 = new System.Windows.Forms.Panel();
+			this.skinnedTabControl1 = new Desktop.Skinning.SkinnedTabControl();
+			this.tabPage1 = new System.Windows.Forms.TabPage();
+			this.skinnedTabControl2 = new Desktop.Skinning.SkinnedTabControl();
+			this.tabPage3 = new System.Windows.Forms.TabPage();
+			this.skinnedSlider2 = new Desktop.Skinning.SkinnedSlider();
+			this.skinnedSlider1 = new Desktop.Skinning.SkinnedSlider();
+			this.accordionListView1 = new Desktop.CommonControls.AccordionListView();
+			this.skinnedGroupBox2 = new Desktop.Skinning.SkinnedGroupBox();
+			this.skinnedComboBox6 = new Desktop.Skinning.SkinnedComboBox();
+			this.skinnedComboBox7 = new Desktop.Skinning.SkinnedComboBox();
+			this.skinnedComboBox8 = new Desktop.Skinning.SkinnedComboBox();
+			this.skinnedComboBox5 = new Desktop.Skinning.SkinnedComboBox();
+			this.skinnedComboBox4 = new Desktop.Skinning.SkinnedComboBox();
+			this.skinnedComboBox3 = new Desktop.Skinning.SkinnedComboBox();
+			this.toolStrip4 = new System.Windows.Forms.ToolStrip();
+			this.toolStripButton10 = new System.Windows.Forms.ToolStripButton();
+			this.toolStripButton11 = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator();
+			this.toolStripSplitButton4 = new System.Windows.Forms.ToolStripSplitButton();
+			this.toolStripMenuItem11 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripMenuItem12 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator();
+			this.toolStripMenuItem13 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripDropDownButton4 = new System.Windows.Forms.ToolStripDropDownButton();
+			this.toolStripMenuItem14 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripMenuItem15 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripButton12 = new System.Windows.Forms.ToolStripButton();
+			this.toolStrip3 = new System.Windows.Forms.ToolStrip();
+			this.toolStripButton7 = new System.Windows.Forms.ToolStripButton();
+			this.toolStripButton8 = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator();
+			this.toolStripSplitButton3 = new System.Windows.Forms.ToolStripSplitButton();
+			this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripSeparator6 = new System.Windows.Forms.ToolStripSeparator();
+			this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripDropDownButton3 = new System.Windows.Forms.ToolStripDropDownButton();
+			this.toolStripMenuItem9 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripMenuItem10 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripButton9 = new System.Windows.Forms.ToolStripButton();
+			this.toolStrip2 = new System.Windows.Forms.ToolStrip();
+			this.toolStripButton4 = new System.Windows.Forms.ToolStripButton();
+			this.toolStripButton5 = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
+			this.toolStripSplitButton2 = new System.Windows.Forms.ToolStripSplitButton();
+			this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
+			this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripDropDownButton2 = new System.Windows.Forms.ToolStripDropDownButton();
+			this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripButton6 = new System.Windows.Forms.ToolStripButton();
+			this.skinnedListView1 = new Desktop.Skinning.SkinnedListView();
+			this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+			this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+			this.skinnedDataGridView1 = new Desktop.Skinning.SkinnedDataGridView();
+			this.Column5 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.Column6 = new Desktop.Skinning.SkinnedDataGridViewComboBoxColumn();
+			this.Column7 = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
+			this.Column8 = new Desktop.Skinning.SkinnedDataGridViewCheckBoxColumn();
+			this.toolStrip1 = new System.Windows.Forms.ToolStrip();
+			this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
+			this.toolStripButton2 = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+			this.toolStripSplitButton1 = new System.Windows.Forms.ToolStripSplitButton();
+			this.itemToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.itemToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
+			this.itemToolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripDropDownButton1 = new System.Windows.Forms.ToolStripDropDownButton();
+			this.item1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.item2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripButton3 = new System.Windows.Forms.ToolStripButton();
+			this.skinnedGroupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedButton8 = new Desktop.Skinning.SkinnedButton();
+			this.skinnedButton9 = new Desktop.Skinning.SkinnedButton();
+			this.skinnedButton10 = new Desktop.Skinning.SkinnedButton();
+			this.skinnedButton5 = new Desktop.Skinning.SkinnedButton();
+			this.skinnedButton6 = new Desktop.Skinning.SkinnedButton();
+			this.skinnedButton7 = new Desktop.Skinning.SkinnedButton();
+			this.skinnedButton4 = new Desktop.Skinning.SkinnedButton();
+			this.skinnedButton3 = new Desktop.Skinning.SkinnedButton();
+			this.skinnedButton2 = new Desktop.Skinning.SkinnedButton();
+			this.skinnedLabel7 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedRadioButton2 = new Desktop.Skinning.SkinnedRadioButton();
+			this.skinnedComboBox2 = new Desktop.Skinning.SkinnedComboBox();
+			this.skinnedRadioButton1 = new Desktop.Skinning.SkinnedRadioButton();
+			this.skinnedLabel4 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedListBox1 = new Desktop.Skinning.SkinnedListBox();
+			this.skinnedLabel6 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedCheckBox1 = new Desktop.Skinning.SkinnedCheckBox();
+			this.skinnedNumericUpDown1 = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.skinnedLabel5 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedLabel3 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedComboBox1 = new Desktop.Skinning.SkinnedComboBox();
+			this.skinnedButton1 = new Desktop.Skinning.SkinnedButton();
+			this.skinnedLabel2 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedTextBox1 = new Desktop.Skinning.SkinnedTextBox();
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.tabPage4 = new System.Windows.Forms.TabPage();
+			this.skinnedTabStrip2 = new Desktop.Skinning.SkinnedTabStrip();
+			this.tabPage2 = new System.Windows.Forms.TabPage();
+			this.skinnedTabStrip1 = new Desktop.Skinning.SkinnedTabStrip();
+			this.cmdDisable = new Desktop.Skinning.SkinnedButton();
+			this.menuStrip1.SuspendLayout();
+			this.statusStrip1.SuspendLayout();
+			this.panel1.SuspendLayout();
+			this.skinnedTabControl1.SuspendLayout();
+			this.tabPage1.SuspendLayout();
+			this.skinnedTabControl2.SuspendLayout();
+			this.tabPage3.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.skinnedSlider2)).BeginInit();
+			((System.ComponentModel.ISupportInitialize)(this.skinnedSlider1)).BeginInit();
+			this.skinnedGroupBox2.SuspendLayout();
+			this.toolStrip4.SuspendLayout();
+			this.toolStrip3.SuspendLayout();
+			this.toolStrip2.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.skinnedDataGridView1)).BeginInit();
+			this.toolStrip1.SuspendLayout();
+			this.skinnedGroupBox1.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.skinnedNumericUpDown1)).BeginInit();
+			this.SuspendLayout();
+			// 
+			// menuButtonToolStripMenuItem
+			// 
+			this.menuButtonToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.menuItemToolStripMenuItem,
+            this.menuItemToolStripMenuItem1});
+			this.menuButtonToolStripMenuItem.Name = "menuButtonToolStripMenuItem";
+			this.menuButtonToolStripMenuItem.Size = new System.Drawing.Size(50, 20);
+			this.menuButtonToolStripMenuItem.Text = "Menu";
+			// 
+			// menuItemToolStripMenuItem
+			// 
+			this.menuItemToolStripMenuItem.Name = "menuItemToolStripMenuItem";
+			this.menuItemToolStripMenuItem.Size = new System.Drawing.Size(132, 22);
+			this.menuItemToolStripMenuItem.Text = "Menu Item";
+			// 
+			// menuItemToolStripMenuItem1
+			// 
+			this.menuItemToolStripMenuItem1.Checked = true;
+			this.menuItemToolStripMenuItem1.CheckOnClick = true;
+			this.menuItemToolStripMenuItem1.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.menuItemToolStripMenuItem1.Name = "menuItemToolStripMenuItem1";
+			this.menuItemToolStripMenuItem1.Size = new System.Drawing.Size(132, 22);
+			this.menuItemToolStripMenuItem1.Text = "Menu Item";
+			// 
+			// menuButtonToolStripMenuItem1
+			// 
+			this.menuButtonToolStripMenuItem1.Name = "menuButtonToolStripMenuItem1";
+			this.menuButtonToolStripMenuItem1.Size = new System.Drawing.Size(55, 20);
+			this.menuButtonToolStripMenuItem1.Text = "Button";
+			// 
+			// menuStrip1
+			// 
+			this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.menuButtonToolStripMenuItem,
+            this.menuButtonToolStripMenuItem1});
+			this.menuStrip1.Location = new System.Drawing.Point(0, 0);
+			this.menuStrip1.Name = "menuStrip1";
+			this.menuStrip1.Size = new System.Drawing.Size(810, 24);
+			this.menuStrip1.TabIndex = 0;
+			this.menuStrip1.Tag = "";
+			this.menuStrip1.Text = "menuStrip1";
+			// 
+			// statusStrip1
+			// 
+			this.statusStrip1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripStatusLabel1});
+			this.statusStrip1.Location = new System.Drawing.Point(0, 648);
+			this.statusStrip1.Name = "statusStrip1";
+			this.statusStrip1.Size = new System.Drawing.Size(810, 22);
+			this.statusStrip1.SizingGrip = false;
+			this.statusStrip1.TabIndex = 15;
+			this.statusStrip1.Text = "statusStrip1";
+			// 
+			// toolStripStatusLabel1
+			// 
+			this.toolStripStatusLabel1.Name = "toolStripStatusLabel1";
+			this.toolStripStatusLabel1.Size = new System.Drawing.Size(50, 17);
+			this.toolStripStatusLabel1.Text = "Message";
+			// 
+			// panel1
+			// 
+			this.panel1.Controls.Add(this.statusStrip1);
+			this.panel1.Controls.Add(this.skinnedTabControl1);
+			this.panel1.Controls.Add(this.skinnedTabStrip1);
+			this.panel1.Controls.Add(this.menuStrip1);
+			this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.panel1.Location = new System.Drawing.Point(0, 0);
+			this.panel1.Name = "panel1";
+			this.panel1.Size = new System.Drawing.Size(810, 670);
+			this.panel1.TabIndex = 16;
+			// 
+			// skinnedTabControl1
+			// 
+			this.skinnedTabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedTabControl1.Controls.Add(this.tabPage1);
+			this.skinnedTabControl1.Controls.Add(this.tabPage2);
+			this.skinnedTabControl1.Location = new System.Drawing.Point(3, 56);
+			this.skinnedTabControl1.Margin = new System.Windows.Forms.Padding(0);
+			this.skinnedTabControl1.Name = "skinnedTabControl1";
+			this.skinnedTabControl1.SelectedIndex = 0;
+			this.skinnedTabControl1.Size = new System.Drawing.Size(804, 789);
+			this.skinnedTabControl1.TabIndex = 2;
+			// 
+			// tabPage1
+			// 
+			this.tabPage1.BackColor = System.Drawing.Color.White;
+			this.tabPage1.Controls.Add(this.skinnedTabControl2);
+			this.tabPage1.Controls.Add(this.skinnedTabStrip2);
+			this.tabPage1.Location = new System.Drawing.Point(4, 22);
+			this.tabPage1.Name = "tabPage1";
+			this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
+			this.tabPage1.Size = new System.Drawing.Size(796, 763);
+			this.tabPage1.TabIndex = 0;
+			this.tabPage1.Text = "Tab 1";
+			// 
+			// skinnedTabControl2
+			// 
+			this.skinnedTabControl2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedTabControl2.Controls.Add(this.tabPage3);
+			this.skinnedTabControl2.Controls.Add(this.tabPage4);
+			this.skinnedTabControl2.Location = new System.Drawing.Point(93, 0);
+			this.skinnedTabControl2.Margin = new System.Windows.Forms.Padding(0);
+			this.skinnedTabControl2.Name = "skinnedTabControl2";
+			this.skinnedTabControl2.SelectedIndex = 0;
+			this.skinnedTabControl2.Size = new System.Drawing.Size(710, 570);
+			this.skinnedTabControl2.TabIndex = 1;
+			// 
+			// tabPage3
+			// 
+			this.tabPage3.BackColor = System.Drawing.Color.White;
+			this.tabPage3.Controls.Add(this.skinnedSlider2);
+			this.tabPage3.Controls.Add(this.skinnedSlider1);
+			this.tabPage3.Controls.Add(this.accordionListView1);
+			this.tabPage3.Controls.Add(this.skinnedGroupBox2);
+			this.tabPage3.Controls.Add(this.toolStrip4);
+			this.tabPage3.Controls.Add(this.toolStrip3);
+			this.tabPage3.Controls.Add(this.toolStrip2);
+			this.tabPage3.Controls.Add(this.skinnedListView1);
+			this.tabPage3.Controls.Add(this.skinnedDataGridView1);
+			this.tabPage3.Controls.Add(this.toolStrip1);
+			this.tabPage3.Controls.Add(this.skinnedGroupBox1);
+			this.tabPage3.Location = new System.Drawing.Point(4, 22);
+			this.tabPage3.Name = "tabPage3";
+			this.tabPage3.Padding = new System.Windows.Forms.Padding(3);
+			this.tabPage3.Size = new System.Drawing.Size(702, 544);
+			this.tabPage3.TabIndex = 1;
+			this.tabPage3.Text = "Tab 3";
+			// 
+			// skinnedSlider2
+			// 
+			this.skinnedSlider2.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.skinnedSlider2.Increment = 10;
+			this.skinnedSlider2.Location = new System.Drawing.Point(325, 363);
+			this.skinnedSlider2.Maximum = 100;
+			this.skinnedSlider2.Minimum = 0;
+			this.skinnedSlider2.Name = "skinnedSlider2";
+			this.skinnedSlider2.Size = new System.Drawing.Size(178, 21);
+			this.skinnedSlider2.TabIndex = 24;
+			this.skinnedSlider2.TickFrequency = 10;
+			this.skinnedSlider2.Value = 0;
+			// 
+			// skinnedSlider1
+			// 
+			this.skinnedSlider1.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedSlider1.Increment = 10;
+			this.skinnedSlider1.Location = new System.Drawing.Point(325, 336);
+			this.skinnedSlider1.Maximum = 100;
+			this.skinnedSlider1.Minimum = 0;
+			this.skinnedSlider1.Name = "skinnedSlider1";
+			this.skinnedSlider1.Size = new System.Drawing.Size(178, 21);
+			this.skinnedSlider1.TabIndex = 23;
+			this.skinnedSlider1.TickFrequency = 10;
+			this.skinnedSlider1.Value = 0;
+			// 
+			// accordionListView1
+			// 
+			this.accordionListView1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+			this.accordionListView1.DataSource = null;
+			this.accordionListView1.Location = new System.Drawing.Point(4, 336);
+			this.accordionListView1.Name = "accordionListView1";
+			this.accordionListView1.SelectedItem = null;
+			this.accordionListView1.Size = new System.Drawing.Size(312, 69);
+			this.accordionListView1.TabIndex = 22;
+			// 
+			// skinnedGroupBox2
+			// 
+			this.skinnedGroupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.skinnedGroupBox2.Controls.Add(this.skinnedComboBox6);
+			this.skinnedGroupBox2.Controls.Add(this.skinnedComboBox7);
+			this.skinnedGroupBox2.Controls.Add(this.skinnedComboBox8);
+			this.skinnedGroupBox2.Controls.Add(this.skinnedComboBox5);
+			this.skinnedGroupBox2.Controls.Add(this.skinnedComboBox4);
+			this.skinnedGroupBox2.Controls.Add(this.skinnedComboBox3);
+			this.skinnedGroupBox2.Location = new System.Drawing.Point(325, 411);
+			this.skinnedGroupBox2.Name = "skinnedGroupBox2";
+			this.skinnedGroupBox2.Size = new System.Drawing.Size(178, 127);
+			this.skinnedGroupBox2.TabIndex = 21;
+			this.skinnedGroupBox2.TabStop = false;
+			this.skinnedGroupBox2.Text = "Combos";
+			// 
+			// skinnedComboBox6
+			// 
+			this.skinnedComboBox6.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.skinnedComboBox6.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.skinnedComboBox6.BackColor = System.Drawing.Color.White;
+			this.skinnedComboBox6.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDown;
+			this.skinnedComboBox6.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.skinnedComboBox6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedComboBox6.Location = new System.Drawing.Point(89, 81);
+			this.skinnedComboBox6.Name = "skinnedComboBox6";
+			this.skinnedComboBox6.SelectedIndex = -1;
+			this.skinnedComboBox6.SelectedItem = null;
+			this.skinnedComboBox6.Size = new System.Drawing.Size(77, 23);
+			this.skinnedComboBox6.Sorted = false;
+			this.skinnedComboBox6.TabIndex = 10;
+			this.skinnedComboBox6.Text = "Text";
+			// 
+			// skinnedComboBox7
+			// 
+			this.skinnedComboBox7.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.skinnedComboBox7.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.skinnedComboBox7.BackColor = System.Drawing.Color.White;
+			this.skinnedComboBox7.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDown;
+			this.skinnedComboBox7.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.skinnedComboBox7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedComboBox7.Location = new System.Drawing.Point(89, 52);
+			this.skinnedComboBox7.Name = "skinnedComboBox7";
+			this.skinnedComboBox7.SelectedIndex = -1;
+			this.skinnedComboBox7.SelectedItem = null;
+			this.skinnedComboBox7.Size = new System.Drawing.Size(77, 23);
+			this.skinnedComboBox7.Sorted = false;
+			this.skinnedComboBox7.TabIndex = 9;
+			this.skinnedComboBox7.Text = "Text";
+			// 
+			// skinnedComboBox8
+			// 
+			this.skinnedComboBox8.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.skinnedComboBox8.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.skinnedComboBox8.BackColor = System.Drawing.Color.White;
+			this.skinnedComboBox8.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDown;
+			this.skinnedComboBox8.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedComboBox8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedComboBox8.Location = new System.Drawing.Point(89, 23);
+			this.skinnedComboBox8.Name = "skinnedComboBox8";
+			this.skinnedComboBox8.SelectedIndex = -1;
+			this.skinnedComboBox8.SelectedItem = null;
+			this.skinnedComboBox8.Size = new System.Drawing.Size(77, 23);
+			this.skinnedComboBox8.Sorted = false;
+			this.skinnedComboBox8.TabIndex = 8;
+			this.skinnedComboBox8.Text = "Text";
+			// 
+			// skinnedComboBox5
+			// 
+			this.skinnedComboBox5.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.skinnedComboBox5.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.skinnedComboBox5.BackColor = System.Drawing.Color.White;
+			this.skinnedComboBox5.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.skinnedComboBox5.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.skinnedComboBox5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedComboBox5.Location = new System.Drawing.Point(6, 81);
+			this.skinnedComboBox5.Name = "skinnedComboBox5";
+			this.skinnedComboBox5.SelectedIndex = -1;
+			this.skinnedComboBox5.SelectedItem = null;
+			this.skinnedComboBox5.Size = new System.Drawing.Size(77, 23);
+			this.skinnedComboBox5.Sorted = false;
+			this.skinnedComboBox5.TabIndex = 7;
+			// 
+			// skinnedComboBox4
+			// 
+			this.skinnedComboBox4.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.skinnedComboBox4.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.skinnedComboBox4.BackColor = System.Drawing.Color.White;
+			this.skinnedComboBox4.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.skinnedComboBox4.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.skinnedComboBox4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedComboBox4.Location = new System.Drawing.Point(6, 52);
+			this.skinnedComboBox4.Name = "skinnedComboBox4";
+			this.skinnedComboBox4.SelectedIndex = -1;
+			this.skinnedComboBox4.SelectedItem = null;
+			this.skinnedComboBox4.Size = new System.Drawing.Size(77, 23);
+			this.skinnedComboBox4.Sorted = false;
+			this.skinnedComboBox4.TabIndex = 6;
+			// 
+			// skinnedComboBox3
+			// 
+			this.skinnedComboBox3.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.skinnedComboBox3.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.skinnedComboBox3.BackColor = System.Drawing.Color.White;
+			this.skinnedComboBox3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.skinnedComboBox3.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedComboBox3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedComboBox3.Location = new System.Drawing.Point(6, 23);
+			this.skinnedComboBox3.Name = "skinnedComboBox3";
+			this.skinnedComboBox3.SelectedIndex = -1;
+			this.skinnedComboBox3.SelectedItem = null;
+			this.skinnedComboBox3.Size = new System.Drawing.Size(77, 23);
+			this.skinnedComboBox3.Sorted = false;
+			this.skinnedComboBox3.TabIndex = 5;
+			// 
+			// toolStrip4
+			// 
+			this.toolStrip4.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
+			this.toolStrip4.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripButton10,
+            this.toolStripButton11,
+            this.toolStripSeparator7,
+            this.toolStripSplitButton4,
+            this.toolStripDropDownButton4,
+            this.toolStripButton12});
+			this.toolStrip4.Location = new System.Drawing.Point(3, 78);
+			this.toolStrip4.Name = "toolStrip4";
+			this.toolStrip4.Size = new System.Drawing.Size(696, 25);
+			this.toolStrip4.TabIndex = 20;
+			this.toolStrip4.Tag = "Surface";
+			this.toolStrip4.Text = "toolStrip4";
+			// 
+			// toolStripButton10
+			// 
+			this.toolStripButton10.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.toolStripButton10.Image = global::Desktop.Properties.Resources.Delete;
+			this.toolStripButton10.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton10.Name = "toolStripButton10";
+			this.toolStripButton10.Size = new System.Drawing.Size(23, 22);
+			this.toolStripButton10.Text = "toolStripButton1";
+			// 
+			// toolStripButton11
+			// 
+			this.toolStripButton11.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripButton11.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton11.Image")));
+			this.toolStripButton11.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton11.Name = "toolStripButton11";
+			this.toolStripButton11.Size = new System.Drawing.Size(47, 22);
+			this.toolStripButton11.Text = "Button";
+			// 
+			// toolStripSeparator7
+			// 
+			this.toolStripSeparator7.Name = "toolStripSeparator7";
+			this.toolStripSeparator7.Size = new System.Drawing.Size(6, 25);
+			// 
+			// toolStripSplitButton4
+			// 
+			this.toolStripSplitButton4.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripSplitButton4.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripMenuItem11,
+            this.toolStripMenuItem12,
+            this.toolStripSeparator8,
+            this.toolStripMenuItem13});
+			this.toolStripSplitButton4.Image = ((System.Drawing.Image)(resources.GetObject("toolStripSplitButton4.Image")));
+			this.toolStripSplitButton4.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripSplitButton4.Name = "toolStripSplitButton4";
+			this.toolStripSplitButton4.Size = new System.Drawing.Size(54, 22);
+			this.toolStripSplitButton4.Text = "Menu";
+			// 
+			// toolStripMenuItem11
+			// 
+			this.toolStripMenuItem11.Name = "toolStripMenuItem11";
+			this.toolStripMenuItem11.Size = new System.Drawing.Size(98, 22);
+			this.toolStripMenuItem11.Text = "Item";
+			// 
+			// toolStripMenuItem12
+			// 
+			this.toolStripMenuItem12.Name = "toolStripMenuItem12";
+			this.toolStripMenuItem12.Size = new System.Drawing.Size(98, 22);
+			this.toolStripMenuItem12.Text = "Item";
+			// 
+			// toolStripSeparator8
+			// 
+			this.toolStripSeparator8.Name = "toolStripSeparator8";
+			this.toolStripSeparator8.Size = new System.Drawing.Size(95, 6);
+			// 
+			// toolStripMenuItem13
+			// 
+			this.toolStripMenuItem13.Name = "toolStripMenuItem13";
+			this.toolStripMenuItem13.Size = new System.Drawing.Size(98, 22);
+			this.toolStripMenuItem13.Text = "Item";
+			// 
+			// toolStripDropDownButton4
+			// 
+			this.toolStripDropDownButton4.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripDropDownButton4.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripMenuItem14,
+            this.toolStripMenuItem15});
+			this.toolStripDropDownButton4.Image = ((System.Drawing.Image)(resources.GetObject("toolStripDropDownButton4.Image")));
+			this.toolStripDropDownButton4.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripDropDownButton4.Name = "toolStripDropDownButton4";
+			this.toolStripDropDownButton4.Size = new System.Drawing.Size(76, 22);
+			this.toolStripDropDownButton4.Text = "Dropdown";
+			// 
+			// toolStripMenuItem14
+			// 
+			this.toolStripMenuItem14.Name = "toolStripMenuItem14";
+			this.toolStripMenuItem14.Size = new System.Drawing.Size(107, 22);
+			this.toolStripMenuItem14.Text = "Item 1";
+			// 
+			// toolStripMenuItem15
+			// 
+			this.toolStripMenuItem15.Checked = true;
+			this.toolStripMenuItem15.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.toolStripMenuItem15.Name = "toolStripMenuItem15";
+			this.toolStripMenuItem15.Size = new System.Drawing.Size(107, 22);
+			this.toolStripMenuItem15.Text = "Item 2";
+			// 
+			// toolStripButton12
+			// 
+			this.toolStripButton12.Checked = true;
+			this.toolStripButton12.CheckOnClick = true;
+			this.toolStripButton12.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.toolStripButton12.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripButton12.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton12.Image")));
+			this.toolStripButton12.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton12.Name = "toolStripButton12";
+			this.toolStripButton12.Size = new System.Drawing.Size(55, 22);
+			this.toolStripButton12.Text = "Selected";
+			// 
+			// toolStrip3
+			// 
+			this.toolStrip3.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
+			this.toolStrip3.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripButton7,
+            this.toolStripButton8,
+            this.toolStripSeparator5,
+            this.toolStripSplitButton3,
+            this.toolStripDropDownButton3,
+            this.toolStripButton9});
+			this.toolStrip3.Location = new System.Drawing.Point(3, 53);
+			this.toolStrip3.Name = "toolStrip3";
+			this.toolStrip3.Size = new System.Drawing.Size(696, 25);
+			this.toolStrip3.TabIndex = 19;
+			this.toolStrip3.Tag = "Secondary";
+			this.toolStrip3.Text = "toolStrip3";
+			// 
+			// toolStripButton7
+			// 
+			this.toolStripButton7.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.toolStripButton7.Image = global::Desktop.Properties.Resources.Delete;
+			this.toolStripButton7.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton7.Name = "toolStripButton7";
+			this.toolStripButton7.Size = new System.Drawing.Size(23, 22);
+			this.toolStripButton7.Text = "toolStripButton1";
+			// 
+			// toolStripButton8
+			// 
+			this.toolStripButton8.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripButton8.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton8.Image")));
+			this.toolStripButton8.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton8.Name = "toolStripButton8";
+			this.toolStripButton8.Size = new System.Drawing.Size(47, 22);
+			this.toolStripButton8.Text = "Button";
+			// 
+			// toolStripSeparator5
+			// 
+			this.toolStripSeparator5.Name = "toolStripSeparator5";
+			this.toolStripSeparator5.Size = new System.Drawing.Size(6, 25);
+			// 
+			// toolStripSplitButton3
+			// 
+			this.toolStripSplitButton3.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripSplitButton3.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripMenuItem6,
+            this.toolStripMenuItem7,
+            this.toolStripSeparator6,
+            this.toolStripMenuItem8});
+			this.toolStripSplitButton3.Image = ((System.Drawing.Image)(resources.GetObject("toolStripSplitButton3.Image")));
+			this.toolStripSplitButton3.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripSplitButton3.Name = "toolStripSplitButton3";
+			this.toolStripSplitButton3.Size = new System.Drawing.Size(54, 22);
+			this.toolStripSplitButton3.Text = "Menu";
+			// 
+			// toolStripMenuItem6
+			// 
+			this.toolStripMenuItem6.Name = "toolStripMenuItem6";
+			this.toolStripMenuItem6.Size = new System.Drawing.Size(98, 22);
+			this.toolStripMenuItem6.Text = "Item";
+			// 
+			// toolStripMenuItem7
+			// 
+			this.toolStripMenuItem7.Name = "toolStripMenuItem7";
+			this.toolStripMenuItem7.Size = new System.Drawing.Size(98, 22);
+			this.toolStripMenuItem7.Text = "Item";
+			// 
+			// toolStripSeparator6
+			// 
+			this.toolStripSeparator6.Name = "toolStripSeparator6";
+			this.toolStripSeparator6.Size = new System.Drawing.Size(95, 6);
+			// 
+			// toolStripMenuItem8
+			// 
+			this.toolStripMenuItem8.Name = "toolStripMenuItem8";
+			this.toolStripMenuItem8.Size = new System.Drawing.Size(98, 22);
+			this.toolStripMenuItem8.Text = "Item";
+			// 
+			// toolStripDropDownButton3
+			// 
+			this.toolStripDropDownButton3.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripDropDownButton3.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripMenuItem9,
+            this.toolStripMenuItem10});
+			this.toolStripDropDownButton3.Image = ((System.Drawing.Image)(resources.GetObject("toolStripDropDownButton3.Image")));
+			this.toolStripDropDownButton3.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripDropDownButton3.Name = "toolStripDropDownButton3";
+			this.toolStripDropDownButton3.Size = new System.Drawing.Size(76, 22);
+			this.toolStripDropDownButton3.Text = "Dropdown";
+			// 
+			// toolStripMenuItem9
+			// 
+			this.toolStripMenuItem9.Name = "toolStripMenuItem9";
+			this.toolStripMenuItem9.Size = new System.Drawing.Size(107, 22);
+			this.toolStripMenuItem9.Text = "Item 1";
+			// 
+			// toolStripMenuItem10
+			// 
+			this.toolStripMenuItem10.Checked = true;
+			this.toolStripMenuItem10.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.toolStripMenuItem10.Name = "toolStripMenuItem10";
+			this.toolStripMenuItem10.Size = new System.Drawing.Size(107, 22);
+			this.toolStripMenuItem10.Text = "Item 2";
+			// 
+			// toolStripButton9
+			// 
+			this.toolStripButton9.Checked = true;
+			this.toolStripButton9.CheckOnClick = true;
+			this.toolStripButton9.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.toolStripButton9.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripButton9.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton9.Image")));
+			this.toolStripButton9.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton9.Name = "toolStripButton9";
+			this.toolStripButton9.Size = new System.Drawing.Size(55, 22);
+			this.toolStripButton9.Text = "Selected";
+			// 
+			// toolStrip2
+			// 
+			this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
+			this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripButton4,
+            this.toolStripButton5,
+            this.toolStripSeparator3,
+            this.toolStripSplitButton2,
+            this.toolStripDropDownButton2,
+            this.toolStripButton6});
+			this.toolStrip2.Location = new System.Drawing.Point(3, 28);
+			this.toolStrip2.Name = "toolStrip2";
+			this.toolStrip2.Size = new System.Drawing.Size(696, 25);
+			this.toolStrip2.TabIndex = 18;
+			this.toolStrip2.Tag = "PrimaryLight";
+			this.toolStrip2.Text = "toolStrip2";
+			// 
+			// toolStripButton4
+			// 
+			this.toolStripButton4.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.toolStripButton4.Image = global::Desktop.Properties.Resources.Delete;
+			this.toolStripButton4.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton4.Name = "toolStripButton4";
+			this.toolStripButton4.Size = new System.Drawing.Size(23, 22);
+			this.toolStripButton4.Text = "toolStripButton1";
+			// 
+			// toolStripButton5
+			// 
+			this.toolStripButton5.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripButton5.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton5.Image")));
+			this.toolStripButton5.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton5.Name = "toolStripButton5";
+			this.toolStripButton5.Size = new System.Drawing.Size(47, 22);
+			this.toolStripButton5.Text = "Button";
+			// 
+			// toolStripSeparator3
+			// 
+			this.toolStripSeparator3.Name = "toolStripSeparator3";
+			this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
+			// 
+			// toolStripSplitButton2
+			// 
+			this.toolStripSplitButton2.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripSplitButton2.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripMenuItem1,
+            this.toolStripMenuItem2,
+            this.toolStripSeparator4,
+            this.toolStripMenuItem3});
+			this.toolStripSplitButton2.Image = ((System.Drawing.Image)(resources.GetObject("toolStripSplitButton2.Image")));
+			this.toolStripSplitButton2.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripSplitButton2.Name = "toolStripSplitButton2";
+			this.toolStripSplitButton2.Size = new System.Drawing.Size(54, 22);
+			this.toolStripSplitButton2.Text = "Menu";
+			// 
+			// toolStripMenuItem1
+			// 
+			this.toolStripMenuItem1.Name = "toolStripMenuItem1";
+			this.toolStripMenuItem1.Size = new System.Drawing.Size(98, 22);
+			this.toolStripMenuItem1.Text = "Item";
+			// 
+			// toolStripMenuItem2
+			// 
+			this.toolStripMenuItem2.Name = "toolStripMenuItem2";
+			this.toolStripMenuItem2.Size = new System.Drawing.Size(98, 22);
+			this.toolStripMenuItem2.Text = "Item";
+			// 
+			// toolStripSeparator4
+			// 
+			this.toolStripSeparator4.Name = "toolStripSeparator4";
+			this.toolStripSeparator4.Size = new System.Drawing.Size(95, 6);
+			// 
+			// toolStripMenuItem3
+			// 
+			this.toolStripMenuItem3.Name = "toolStripMenuItem3";
+			this.toolStripMenuItem3.Size = new System.Drawing.Size(98, 22);
+			this.toolStripMenuItem3.Text = "Item";
+			// 
+			// toolStripDropDownButton2
+			// 
+			this.toolStripDropDownButton2.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripDropDownButton2.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripMenuItem4,
+            this.toolStripMenuItem5});
+			this.toolStripDropDownButton2.Image = ((System.Drawing.Image)(resources.GetObject("toolStripDropDownButton2.Image")));
+			this.toolStripDropDownButton2.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripDropDownButton2.Name = "toolStripDropDownButton2";
+			this.toolStripDropDownButton2.Size = new System.Drawing.Size(76, 22);
+			this.toolStripDropDownButton2.Text = "Dropdown";
+			// 
+			// toolStripMenuItem4
+			// 
+			this.toolStripMenuItem4.Name = "toolStripMenuItem4";
+			this.toolStripMenuItem4.Size = new System.Drawing.Size(107, 22);
+			this.toolStripMenuItem4.Text = "Item 1";
+			// 
+			// toolStripMenuItem5
+			// 
+			this.toolStripMenuItem5.Checked = true;
+			this.toolStripMenuItem5.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.toolStripMenuItem5.Name = "toolStripMenuItem5";
+			this.toolStripMenuItem5.Size = new System.Drawing.Size(107, 22);
+			this.toolStripMenuItem5.Text = "Item 2";
+			// 
+			// toolStripButton6
+			// 
+			this.toolStripButton6.Checked = true;
+			this.toolStripButton6.CheckOnClick = true;
+			this.toolStripButton6.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.toolStripButton6.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripButton6.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton6.Image")));
+			this.toolStripButton6.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton6.Name = "toolStripButton6";
+			this.toolStripButton6.Size = new System.Drawing.Size(55, 22);
+			this.toolStripButton6.Text = "Selected";
+			// 
+			// skinnedListView1
+			// 
+			this.skinnedListView1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.skinnedListView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+            this.columnHeader1,
+            this.columnHeader2});
+			this.skinnedListView1.FullRowSelect = true;
+			listViewGroup1.Header = "Group 1";
+			listViewGroup1.Name = "Group1";
+			listViewGroup2.Header = "Group 2";
+			listViewGroup2.Name = "Group2";
+			this.skinnedListView1.Groups.AddRange(new System.Windows.Forms.ListViewGroup[] {
+            listViewGroup1,
+            listViewGroup2});
+			this.skinnedListView1.HideSelection = false;
+			listViewItem1.Group = listViewGroup1;
+			listViewItem1.StateImageIndex = 0;
+			listViewItem2.Group = listViewGroup2;
+			listViewItem2.StateImageIndex = 0;
+			this.skinnedListView1.Items.AddRange(new System.Windows.Forms.ListViewItem[] {
+            listViewItem1,
+            listViewItem2});
+			this.skinnedListView1.Location = new System.Drawing.Point(6, 411);
+			this.skinnedListView1.MultiSelect = false;
+			this.skinnedListView1.Name = "skinnedListView1";
+			this.skinnedListView1.OwnerDraw = true;
+			this.skinnedListView1.Size = new System.Drawing.Size(310, 128);
+			this.skinnedListView1.TabIndex = 17;
+			this.skinnedListView1.UseCompatibleStateImageBehavior = false;
+			this.skinnedListView1.View = System.Windows.Forms.View.Details;
+			// 
+			// columnHeader1
+			// 
+			this.columnHeader1.Text = "Header 1";
+			this.columnHeader1.Width = 200;
+			// 
+			// columnHeader2
+			// 
+			this.columnHeader2.Text = "Header 2";
+			this.columnHeader2.Width = 80;
+			// 
+			// skinnedDataGridView1
+			// 
+			this.skinnedDataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedDataGridView1.BackgroundColor = System.Drawing.Color.FromArgb(((int)(((byte)(245)))), ((int)(((byte)(245)))), ((int)(((byte)(245)))));
+			this.skinnedDataGridView1.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.skinnedDataGridView1.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(245)))), ((int)(((byte)(245)))), ((int)(((byte)(245)))));
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Arial", 9F);
+			dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.skinnedDataGridView1.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
+			this.skinnedDataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+			this.skinnedDataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.Column5,
+            this.Column6,
+            this.Column7,
+            this.Column8});
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle2.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle2.Font = new System.Drawing.Font("Arial", 9F);
+			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.skinnedDataGridView1.DefaultCellStyle = dataGridViewCellStyle2;
+			this.skinnedDataGridView1.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
+			this.skinnedDataGridView1.EnableHeadersVisualStyles = false;
+			this.skinnedDataGridView1.Font = new System.Drawing.Font("Arial", 9F);
+			this.skinnedDataGridView1.GridColor = System.Drawing.Color.FromArgb(((int)(((byte)(142)))), ((int)(((byte)(153)))), ((int)(((byte)(243)))));
+			this.skinnedDataGridView1.Location = new System.Drawing.Point(3, 227);
+			this.skinnedDataGridView1.Name = "skinnedDataGridView1";
+			this.skinnedDataGridView1.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(245)))), ((int)(((byte)(245)))), ((int)(((byte)(245)))));
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Arial", 9F);
+			dataGridViewCellStyle3.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.skinnedDataGridView1.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
+			this.skinnedDataGridView1.Size = new System.Drawing.Size(687, 103);
+			this.skinnedDataGridView1.TabIndex = 16;
+			// 
+			// Column5
+			// 
+			this.Column5.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.Column5.HeaderText = "Column5";
+			this.Column5.Name = "Column5";
+			// 
+			// Column6
+			// 
+			this.Column6.AutoComplete = false;
+			this.Column6.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.Column6.DisplayMember = null;
+			this.Column6.HeaderText = "Column6";
+			this.Column6.Name = "Column6";
+			this.Column6.Sorted = false;
+			// 
+			// Column7
+			// 
+			this.Column7.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.Column7.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.Column7.Flat = false;
+			this.Column7.HeaderText = "Column7";
+			this.Column7.Name = "Column7";
+			// 
+			// Column8
+			// 
+			this.Column8.HeaderText = "Column8";
+			this.Column8.Name = "Column8";
+			this.Column8.Width = 22;
+			// 
+			// toolStrip1
+			// 
+			this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
+			this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.toolStripButton1,
+            this.toolStripButton2,
+            this.toolStripSeparator1,
+            this.toolStripSplitButton1,
+            this.toolStripDropDownButton1,
+            this.toolStripButton3});
+			this.toolStrip1.Location = new System.Drawing.Point(3, 3);
+			this.toolStrip1.Name = "toolStrip1";
+			this.toolStrip1.Size = new System.Drawing.Size(696, 25);
+			this.toolStrip1.TabIndex = 15;
+			this.toolStrip1.Tag = "PrimaryDark";
+			this.toolStrip1.Text = "toolStrip1";
+			// 
+			// toolStripButton1
+			// 
+			this.toolStripButton1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.toolStripButton1.Image = global::Desktop.Properties.Resources.Delete;
+			this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton1.Name = "toolStripButton1";
+			this.toolStripButton1.Size = new System.Drawing.Size(23, 22);
+			this.toolStripButton1.Text = "toolStripButton1";
+			// 
+			// toolStripButton2
+			// 
+			this.toolStripButton2.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripButton2.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton2.Image")));
+			this.toolStripButton2.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton2.Name = "toolStripButton2";
+			this.toolStripButton2.Size = new System.Drawing.Size(47, 22);
+			this.toolStripButton2.Text = "Button";
+			// 
+			// toolStripSeparator1
+			// 
+			this.toolStripSeparator1.Name = "toolStripSeparator1";
+			this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
+			// 
+			// toolStripSplitButton1
+			// 
+			this.toolStripSplitButton1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripSplitButton1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.itemToolStripMenuItem,
+            this.itemToolStripMenuItem1,
+            this.toolStripSeparator2,
+            this.itemToolStripMenuItem2});
+			this.toolStripSplitButton1.Image = ((System.Drawing.Image)(resources.GetObject("toolStripSplitButton1.Image")));
+			this.toolStripSplitButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripSplitButton1.Name = "toolStripSplitButton1";
+			this.toolStripSplitButton1.Size = new System.Drawing.Size(54, 22);
+			this.toolStripSplitButton1.Text = "Menu";
+			// 
+			// itemToolStripMenuItem
+			// 
+			this.itemToolStripMenuItem.Name = "itemToolStripMenuItem";
+			this.itemToolStripMenuItem.Size = new System.Drawing.Size(98, 22);
+			this.itemToolStripMenuItem.Text = "Item";
+			// 
+			// itemToolStripMenuItem1
+			// 
+			this.itemToolStripMenuItem1.Name = "itemToolStripMenuItem1";
+			this.itemToolStripMenuItem1.Size = new System.Drawing.Size(98, 22);
+			this.itemToolStripMenuItem1.Text = "Item";
+			// 
+			// toolStripSeparator2
+			// 
+			this.toolStripSeparator2.Name = "toolStripSeparator2";
+			this.toolStripSeparator2.Size = new System.Drawing.Size(95, 6);
+			// 
+			// itemToolStripMenuItem2
+			// 
+			this.itemToolStripMenuItem2.Name = "itemToolStripMenuItem2";
+			this.itemToolStripMenuItem2.Size = new System.Drawing.Size(98, 22);
+			this.itemToolStripMenuItem2.Text = "Item";
+			// 
+			// toolStripDropDownButton1
+			// 
+			this.toolStripDropDownButton1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripDropDownButton1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.item1ToolStripMenuItem,
+            this.item2ToolStripMenuItem});
+			this.toolStripDropDownButton1.Image = ((System.Drawing.Image)(resources.GetObject("toolStripDropDownButton1.Image")));
+			this.toolStripDropDownButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripDropDownButton1.Name = "toolStripDropDownButton1";
+			this.toolStripDropDownButton1.Size = new System.Drawing.Size(76, 22);
+			this.toolStripDropDownButton1.Text = "Dropdown";
+			// 
+			// item1ToolStripMenuItem
+			// 
+			this.item1ToolStripMenuItem.Name = "item1ToolStripMenuItem";
+			this.item1ToolStripMenuItem.Size = new System.Drawing.Size(107, 22);
+			this.item1ToolStripMenuItem.Text = "Item 1";
+			// 
+			// item2ToolStripMenuItem
+			// 
+			this.item2ToolStripMenuItem.Checked = true;
+			this.item2ToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.item2ToolStripMenuItem.Name = "item2ToolStripMenuItem";
+			this.item2ToolStripMenuItem.Size = new System.Drawing.Size(107, 22);
+			this.item2ToolStripMenuItem.Text = "Item 2";
+			// 
+			// toolStripButton3
+			// 
+			this.toolStripButton3.Checked = true;
+			this.toolStripButton3.CheckOnClick = true;
+			this.toolStripButton3.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.toolStripButton3.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.toolStripButton3.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton3.Image")));
+			this.toolStripButton3.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton3.Name = "toolStripButton3";
+			this.toolStripButton3.Size = new System.Drawing.Size(55, 22);
+			this.toolStripButton3.Text = "Selected";
+			// 
+			// skinnedGroupBox1
+			// 
+			this.skinnedGroupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedGroupBox1.Controls.Add(this.skinnedPanel1);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedButton5);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedButton6);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedButton7);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedButton4);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedButton3);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedButton2);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedLabel7);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedRadioButton2);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedComboBox2);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedRadioButton1);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedLabel4);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedListBox1);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedLabel6);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedCheckBox1);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedNumericUpDown1);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedLabel5);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedLabel3);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedComboBox1);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedButton1);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedLabel2);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedTextBox1);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedLabel1);
+			this.skinnedGroupBox1.Location = new System.Drawing.Point(6, 113);
+			this.skinnedGroupBox1.Name = "skinnedGroupBox1";
+			this.skinnedGroupBox1.Size = new System.Drawing.Size(690, 112);
+			this.skinnedGroupBox1.TabIndex = 0;
+			this.skinnedGroupBox1.TabStop = false;
+			this.skinnedGroupBox1.Text = "Section";
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.skinnedButton8);
+			this.skinnedPanel1.Controls.Add(this.skinnedButton9);
+			this.skinnedPanel1.Controls.Add(this.skinnedButton10);
+			this.skinnedPanel1.Location = new System.Drawing.Point(596, 5);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(88, 100);
+			this.skinnedPanel1.TabIndex = 22;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// skinnedButton8
+			// 
+			this.skinnedButton8.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedButton8.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.skinnedButton8.Flat = true;
+			this.skinnedButton8.ForeColor = System.Drawing.Color.White;
+			this.skinnedButton8.Location = new System.Drawing.Point(8, 67);
+			this.skinnedButton8.Name = "skinnedButton8";
+			this.skinnedButton8.Size = new System.Drawing.Size(72, 23);
+			this.skinnedButton8.TabIndex = 22;
+			this.skinnedButton8.Text = "Button";
+			this.skinnedButton8.UseVisualStyleBackColor = true;
+			// 
+			// skinnedButton9
+			// 
+			this.skinnedButton9.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedButton9.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.skinnedButton9.Flat = true;
+			this.skinnedButton9.ForeColor = System.Drawing.Color.Red;
+			this.skinnedButton9.Location = new System.Drawing.Point(8, 38);
+			this.skinnedButton9.Name = "skinnedButton9";
+			this.skinnedButton9.Size = new System.Drawing.Size(72, 23);
+			this.skinnedButton9.TabIndex = 21;
+			this.skinnedButton9.Text = "Button";
+			this.skinnedButton9.UseVisualStyleBackColor = true;
+			// 
+			// skinnedButton10
+			// 
+			this.skinnedButton10.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedButton10.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedButton10.Flat = true;
+			this.skinnedButton10.ForeColor = System.Drawing.Color.White;
+			this.skinnedButton10.Location = new System.Drawing.Point(8, 10);
+			this.skinnedButton10.Name = "skinnedButton10";
+			this.skinnedButton10.Size = new System.Drawing.Size(72, 23);
+			this.skinnedButton10.TabIndex = 20;
+			this.skinnedButton10.Text = "Button";
+			this.skinnedButton10.UseVisualStyleBackColor = true;
+			// 
+			// skinnedButton5
+			// 
+			this.skinnedButton5.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedButton5.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.skinnedButton5.Flat = true;
+			this.skinnedButton5.ForeColor = System.Drawing.Color.Black;
+			this.skinnedButton5.Location = new System.Drawing.Point(518, 73);
+			this.skinnedButton5.Name = "skinnedButton5";
+			this.skinnedButton5.Size = new System.Drawing.Size(72, 23);
+			this.skinnedButton5.TabIndex = 19;
+			this.skinnedButton5.Text = "Button";
+			this.skinnedButton5.UseVisualStyleBackColor = true;
+			// 
+			// skinnedButton6
+			// 
+			this.skinnedButton6.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedButton6.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.skinnedButton6.Flat = true;
+			this.skinnedButton6.ForeColor = System.Drawing.Color.Red;
+			this.skinnedButton6.Location = new System.Drawing.Point(518, 44);
+			this.skinnedButton6.Name = "skinnedButton6";
+			this.skinnedButton6.Size = new System.Drawing.Size(72, 23);
+			this.skinnedButton6.TabIndex = 18;
+			this.skinnedButton6.Text = "Button";
+			this.skinnedButton6.UseVisualStyleBackColor = true;
+			// 
+			// skinnedButton7
+			// 
+			this.skinnedButton7.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedButton7.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedButton7.Flat = true;
+			this.skinnedButton7.ForeColor = System.Drawing.Color.Blue;
+			this.skinnedButton7.Location = new System.Drawing.Point(518, 16);
+			this.skinnedButton7.Name = "skinnedButton7";
+			this.skinnedButton7.Size = new System.Drawing.Size(72, 23);
+			this.skinnedButton7.TabIndex = 17;
+			this.skinnedButton7.Text = "Button";
+			this.skinnedButton7.UseVisualStyleBackColor = true;
+			// 
+			// skinnedButton4
+			// 
+			this.skinnedButton4.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedButton4.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.skinnedButton4.Flat = false;
+			this.skinnedButton4.Location = new System.Drawing.Point(440, 73);
+			this.skinnedButton4.Name = "skinnedButton4";
+			this.skinnedButton4.Size = new System.Drawing.Size(72, 23);
+			this.skinnedButton4.TabIndex = 16;
+			this.skinnedButton4.Text = "Button";
+			this.skinnedButton4.UseVisualStyleBackColor = true;
+			// 
+			// skinnedButton3
+			// 
+			this.skinnedButton3.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedButton3.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.skinnedButton3.Flat = false;
+			this.skinnedButton3.Location = new System.Drawing.Point(440, 44);
+			this.skinnedButton3.Name = "skinnedButton3";
+			this.skinnedButton3.Size = new System.Drawing.Size(72, 23);
+			this.skinnedButton3.TabIndex = 15;
+			this.skinnedButton3.Text = "Button";
+			this.skinnedButton3.UseVisualStyleBackColor = true;
+			// 
+			// skinnedButton2
+			// 
+			this.skinnedButton2.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedButton2.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedButton2.Flat = false;
+			this.skinnedButton2.Location = new System.Drawing.Point(440, 16);
+			this.skinnedButton2.Name = "skinnedButton2";
+			this.skinnedButton2.Size = new System.Drawing.Size(72, 23);
+			this.skinnedButton2.TabIndex = 14;
+			this.skinnedButton2.Text = "Button";
+			this.skinnedButton2.UseVisualStyleBackColor = true;
+			// 
+			// skinnedLabel7
+			// 
+			this.skinnedLabel7.AutoSize = true;
+			this.skinnedLabel7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel7.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel7.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel7.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.skinnedLabel7.Location = new System.Drawing.Point(292, 21);
+			this.skinnedLabel7.Name = "skinnedLabel7";
+			this.skinnedLabel7.Size = new System.Drawing.Size(36, 13);
+			this.skinnedLabel7.TabIndex = 11;
+			this.skinnedLabel7.Text = "Label:";
+			// 
+			// skinnedRadioButton2
+			// 
+			this.skinnedRadioButton2.AutoSize = true;
+			this.skinnedRadioButton2.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedRadioButton2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedRadioButton2.Location = new System.Drawing.Point(362, 88);
+			this.skinnedRadioButton2.Name = "skinnedRadioButton2";
+			this.skinnedRadioButton2.Size = new System.Drawing.Size(65, 17);
+			this.skinnedRadioButton2.TabIndex = 4;
+			this.skinnedRadioButton2.Text = "Option 2";
+			this.skinnedRadioButton2.UseVisualStyleBackColor = true;
+			// 
+			// skinnedComboBox2
+			// 
+			this.skinnedComboBox2.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.skinnedComboBox2.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.skinnedComboBox2.BackColor = System.Drawing.Color.White;
+			this.skinnedComboBox2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDown;
+			this.skinnedComboBox2.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedComboBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedComboBox2.Location = new System.Drawing.Point(334, 16);
+			this.skinnedComboBox2.Name = "skinnedComboBox2";
+			this.skinnedComboBox2.SelectedIndex = -1;
+			this.skinnedComboBox2.SelectedItem = null;
+			this.skinnedComboBox2.Size = new System.Drawing.Size(100, 21);
+			this.skinnedComboBox2.Sorted = false;
+			this.skinnedComboBox2.TabIndex = 10;
+			this.skinnedComboBox2.Text = "Item 1";
+			// 
+			// skinnedRadioButton1
+			// 
+			this.skinnedRadioButton1.AutoSize = true;
+			this.skinnedRadioButton1.Checked = true;
+			this.skinnedRadioButton1.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedRadioButton1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedRadioButton1.Location = new System.Drawing.Point(291, 88);
+			this.skinnedRadioButton1.Name = "skinnedRadioButton1";
+			this.skinnedRadioButton1.Size = new System.Drawing.Size(65, 17);
+			this.skinnedRadioButton1.TabIndex = 3;
+			this.skinnedRadioButton1.TabStop = true;
+			this.skinnedRadioButton1.Text = "Option 1";
+			this.skinnedRadioButton1.UseVisualStyleBackColor = true;
+			// 
+			// skinnedLabel4
+			// 
+			this.skinnedLabel4.AutoSize = true;
+			this.skinnedLabel4.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.skinnedLabel4.ForeColor = System.Drawing.Color.Blue;
+			this.skinnedLabel4.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel4.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.skinnedLabel4.Location = new System.Drawing.Point(292, 45);
+			this.skinnedLabel4.Name = "skinnedLabel4";
+			this.skinnedLabel4.Size = new System.Drawing.Size(95, 21);
+			this.skinnedLabel4.TabIndex = 13;
+			this.skinnedLabel4.Text = "Group Label";
+			// 
+			// skinnedListBox1
+			// 
+			this.skinnedListBox1.BackColor = System.Drawing.Color.White;
+			this.skinnedListBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedListBox1.ForeColor = System.Drawing.Color.Black;
+			this.skinnedListBox1.FormattingEnabled = true;
+			this.skinnedListBox1.Items.AddRange(new object[] {
+            "Item 1",
+            "Item 2"});
+			this.skinnedListBox1.Location = new System.Drawing.Point(205, 49);
+			this.skinnedListBox1.Name = "skinnedListBox1";
+			this.skinnedListBox1.Size = new System.Drawing.Size(75, 43);
+			this.skinnedListBox1.TabIndex = 9;
+			// 
+			// skinnedLabel6
+			// 
+			this.skinnedLabel6.AutoSize = true;
+			this.skinnedLabel6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel6.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel6.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel6.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.skinnedLabel6.Location = new System.Drawing.Point(163, 54);
+			this.skinnedLabel6.Name = "skinnedLabel6";
+			this.skinnedLabel6.Size = new System.Drawing.Size(36, 13);
+			this.skinnedLabel6.TabIndex = 8;
+			this.skinnedLabel6.Text = "Label:";
+			// 
+			// skinnedCheckBox1
+			// 
+			this.skinnedCheckBox1.AutoSize = true;
+			this.skinnedCheckBox1.Checked = true;
+			this.skinnedCheckBox1.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.skinnedCheckBox1.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedCheckBox1.Location = new System.Drawing.Point(291, 65);
+			this.skinnedCheckBox1.Name = "skinnedCheckBox1";
+			this.skinnedCheckBox1.Size = new System.Drawing.Size(66, 17);
+			this.skinnedCheckBox1.TabIndex = 0;
+			this.skinnedCheckBox1.Text = "Check 1";
+			this.skinnedCheckBox1.UseVisualStyleBackColor = true;
+			// 
+			// skinnedNumericUpDown1
+			// 
+			this.skinnedNumericUpDown1.BackColor = System.Drawing.Color.White;
+			this.skinnedNumericUpDown1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedNumericUpDown1.ForeColor = System.Drawing.Color.Black;
+			this.skinnedNumericUpDown1.Location = new System.Drawing.Point(205, 23);
+			this.skinnedNumericUpDown1.Name = "skinnedNumericUpDown1";
+			this.skinnedNumericUpDown1.Size = new System.Drawing.Size(75, 20);
+			this.skinnedNumericUpDown1.TabIndex = 7;
+			// 
+			// skinnedLabel5
+			// 
+			this.skinnedLabel5.AutoSize = true;
+			this.skinnedLabel5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel5.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel5.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel5.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.skinnedLabel5.Location = new System.Drawing.Point(163, 26);
+			this.skinnedLabel5.Name = "skinnedLabel5";
+			this.skinnedLabel5.Size = new System.Drawing.Size(36, 13);
+			this.skinnedLabel5.TabIndex = 6;
+			this.skinnedLabel5.Text = "Label:";
+			// 
+			// skinnedLabel3
+			// 
+			this.skinnedLabel3.AutoSize = true;
+			this.skinnedLabel3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel3.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.skinnedLabel3.Location = new System.Drawing.Point(6, 83);
+			this.skinnedLabel3.Name = "skinnedLabel3";
+			this.skinnedLabel3.Size = new System.Drawing.Size(36, 13);
+			this.skinnedLabel3.TabIndex = 5;
+			this.skinnedLabel3.Text = "Label:";
+			// 
+			// skinnedComboBox1
+			// 
+			this.skinnedComboBox1.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.skinnedComboBox1.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.skinnedComboBox1.BackColor = System.Drawing.Color.White;
+			this.skinnedComboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.skinnedComboBox1.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedComboBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedComboBox1.Location = new System.Drawing.Point(48, 78);
+			this.skinnedComboBox1.Name = "skinnedComboBox1";
+			this.skinnedComboBox1.SelectedIndex = -1;
+			this.skinnedComboBox1.SelectedItem = null;
+			this.skinnedComboBox1.Size = new System.Drawing.Size(100, 21);
+			this.skinnedComboBox1.Sorted = false;
+			this.skinnedComboBox1.TabIndex = 4;
+			this.skinnedComboBox1.Text = "skinnedComboBox1";
+			// 
+			// skinnedButton1
+			// 
+			this.skinnedButton1.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedButton1.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.skinnedButton1.Flat = false;
+			this.skinnedButton1.Location = new System.Drawing.Point(48, 49);
+			this.skinnedButton1.Name = "skinnedButton1";
+			this.skinnedButton1.Size = new System.Drawing.Size(100, 21);
+			this.skinnedButton1.TabIndex = 3;
+			this.skinnedButton1.Text = "Button";
+			this.skinnedButton1.UseVisualStyleBackColor = true;
+			// 
+			// skinnedLabel2
+			// 
+			this.skinnedLabel2.AutoSize = true;
+			this.skinnedLabel2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel2.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.skinnedLabel2.Location = new System.Drawing.Point(6, 54);
+			this.skinnedLabel2.Name = "skinnedLabel2";
+			this.skinnedLabel2.Size = new System.Drawing.Size(36, 13);
+			this.skinnedLabel2.TabIndex = 2;
+			this.skinnedLabel2.Text = "Label:";
+			// 
+			// skinnedTextBox1
+			// 
+			this.skinnedTextBox1.BackColor = System.Drawing.Color.White;
+			this.skinnedTextBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedTextBox1.ForeColor = System.Drawing.Color.Black;
+			this.skinnedTextBox1.Location = new System.Drawing.Point(48, 23);
+			this.skinnedTextBox1.Name = "skinnedTextBox1";
+			this.skinnedTextBox1.Size = new System.Drawing.Size(100, 20);
+			this.skinnedTextBox1.TabIndex = 1;
+			this.skinnedTextBox1.Text = "Text";
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.AutoSize = true;
+			this.skinnedLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.skinnedLabel1.Location = new System.Drawing.Point(6, 26);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(36, 13);
+			this.skinnedLabel1.TabIndex = 0;
+			this.skinnedLabel1.Text = "Label:";
+			// 
+			// tabPage4
+			// 
+			this.tabPage4.BackColor = System.Drawing.Color.White;
+			this.tabPage4.Location = new System.Drawing.Point(4, 22);
+			this.tabPage4.Name = "tabPage4";
+			this.tabPage4.Padding = new System.Windows.Forms.Padding(3);
+			this.tabPage4.Size = new System.Drawing.Size(702, 544);
+			this.tabPage4.TabIndex = 0;
+			this.tabPage4.Text = "Tab 4";
+			// 
+			// skinnedTabStrip2
+			// 
+			this.skinnedTabStrip2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+			this.skinnedTabStrip2.Location = new System.Drawing.Point(4, 3);
+			this.skinnedTabStrip2.Margin = new System.Windows.Forms.Padding(0);
+			this.skinnedTabStrip2.Name = "skinnedTabStrip2";
+			this.skinnedTabStrip2.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.skinnedTabStrip2.ShowCloseButton = false;
+			this.skinnedTabStrip2.Size = new System.Drawing.Size(89, 757);
+			this.skinnedTabStrip2.StartMargin = 5;
+			this.skinnedTabStrip2.TabControl = this.skinnedTabControl2;
+			this.skinnedTabStrip2.TabIndex = 0;
+			this.skinnedTabStrip2.TabMargin = 5;
+			this.skinnedTabStrip2.TabPadding = 20;
+			this.skinnedTabStrip2.TabSize = 25;
+			this.skinnedTabStrip2.TabType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.skinnedTabStrip2.Text = "skinnedTabStrip2";
+			this.skinnedTabStrip2.Vertical = true;
+			// 
+			// tabPage2
+			// 
+			this.tabPage2.BackColor = System.Drawing.Color.White;
+			this.tabPage2.Location = new System.Drawing.Point(4, 22);
+			this.tabPage2.Name = "tabPage2";
+			this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
+			this.tabPage2.Size = new System.Drawing.Size(796, 763);
+			this.tabPage2.TabIndex = 1;
+			this.tabPage2.Text = "Tab 2";
+			// 
+			// skinnedTabStrip1
+			// 
+			this.skinnedTabStrip1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedTabStrip1.Location = new System.Drawing.Point(0, 24);
+			this.skinnedTabStrip1.Margin = new System.Windows.Forms.Padding(0);
+			this.skinnedTabStrip1.Name = "skinnedTabStrip1";
+			this.skinnedTabStrip1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
+			this.skinnedTabStrip1.ShowCloseButton = false;
+			this.skinnedTabStrip1.Size = new System.Drawing.Size(810, 32);
+			this.skinnedTabStrip1.StartMargin = 5;
+			this.skinnedTabStrip1.TabControl = this.skinnedTabControl1;
+			this.skinnedTabStrip1.TabIndex = 1;
+			this.skinnedTabStrip1.TabMargin = 5;
+			this.skinnedTabStrip1.TabPadding = 20;
+			this.skinnedTabStrip1.TabSize = -1;
+			this.skinnedTabStrip1.TabType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.skinnedTabStrip1.Text = "skinnedTabStrip1";
+			this.skinnedTabStrip1.Vertical = false;
+			// 
+			// cmdDisable
+			// 
+			this.cmdDisable.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdDisable.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdDisable.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdDisable.Flat = false;
+			this.cmdDisable.Location = new System.Drawing.Point(727, 621);
+			this.cmdDisable.Name = "cmdDisable";
+			this.cmdDisable.Size = new System.Drawing.Size(75, 23);
+			this.cmdDisable.TabIndex = 16;
+			this.cmdDisable.Text = "Toggle";
+			this.cmdDisable.UseVisualStyleBackColor = true;
+			this.cmdDisable.Click += new System.EventHandler(this.cmdDisable_Click);
+			// 
+			// SkinTester
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.cmdDisable);
+			this.Controls.Add(this.panel1);
+			this.Name = "SkinTester";
+			this.Size = new System.Drawing.Size(810, 670);
+			this.menuStrip1.ResumeLayout(false);
+			this.menuStrip1.PerformLayout();
+			this.statusStrip1.ResumeLayout(false);
+			this.statusStrip1.PerformLayout();
+			this.panel1.ResumeLayout(false);
+			this.panel1.PerformLayout();
+			this.skinnedTabControl1.ResumeLayout(false);
+			this.tabPage1.ResumeLayout(false);
+			this.skinnedTabControl2.ResumeLayout(false);
+			this.tabPage3.ResumeLayout(false);
+			this.tabPage3.PerformLayout();
+			((System.ComponentModel.ISupportInitialize)(this.skinnedSlider2)).EndInit();
+			((System.ComponentModel.ISupportInitialize)(this.skinnedSlider1)).EndInit();
+			this.skinnedGroupBox2.ResumeLayout(false);
+			this.toolStrip4.ResumeLayout(false);
+			this.toolStrip4.PerformLayout();
+			this.toolStrip3.ResumeLayout(false);
+			this.toolStrip3.PerformLayout();
+			this.toolStrip2.ResumeLayout(false);
+			this.toolStrip2.PerformLayout();
+			((System.ComponentModel.ISupportInitialize)(this.skinnedDataGridView1)).EndInit();
+			this.toolStrip1.ResumeLayout(false);
+			this.toolStrip1.PerformLayout();
+			this.skinnedGroupBox1.ResumeLayout(false);
+			this.skinnedGroupBox1.PerformLayout();
+			this.skinnedPanel1.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.skinnedNumericUpDown1)).EndInit();
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private System.Windows.Forms.ToolStripMenuItem menuButtonToolStripMenuItem;
+		private System.Windows.Forms.ToolStripMenuItem menuItemToolStripMenuItem;
+		private System.Windows.Forms.ToolStripMenuItem menuItemToolStripMenuItem1;
+		private System.Windows.Forms.ToolStripMenuItem menuButtonToolStripMenuItem1;
+		private System.Windows.Forms.MenuStrip menuStrip1;
+		private SkinnedTabStrip skinnedTabStrip1;
+		private SkinnedTabControl skinnedTabControl1;
+		private System.Windows.Forms.TabPage tabPage1;
+		private SkinnedTabControl skinnedTabControl2;
+		private System.Windows.Forms.TabPage tabPage4;
+		private System.Windows.Forms.TabPage tabPage3;
+		private SkinnedTabStrip skinnedTabStrip2;
+		private System.Windows.Forms.TabPage tabPage2;
+		private SkinnedGroupBox skinnedGroupBox1;
+		private SkinnedLabel skinnedLabel2;
+		private SkinnedTextBox skinnedTextBox1;
+		private SkinnedLabel skinnedLabel1;
+		private SkinnedButton skinnedButton1;
+		private SkinnedComboBox skinnedComboBox1;
+		private SkinnedLabel skinnedLabel3;
+		private SkinnedRadioButton skinnedRadioButton2;
+		private SkinnedRadioButton skinnedRadioButton1;
+		private SkinnedCheckBox skinnedCheckBox1;
+		private SkinnedLabel skinnedLabel4;
+		private SkinnedListBox skinnedListBox1;
+		private SkinnedLabel skinnedLabel6;
+		private SkinnedNumericUpDown skinnedNumericUpDown1;
+		private SkinnedLabel skinnedLabel5;
+		private System.Windows.Forms.ToolStrip toolStrip1;
+		private System.Windows.Forms.ToolStripButton toolStripButton1;
+		private System.Windows.Forms.ToolStripButton toolStripButton2;
+		private System.Windows.Forms.ToolStripSplitButton toolStripSplitButton1;
+		private System.Windows.Forms.ToolStripMenuItem itemToolStripMenuItem;
+		private System.Windows.Forms.ToolStripMenuItem itemToolStripMenuItem1;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
+		private System.Windows.Forms.ToolStripMenuItem itemToolStripMenuItem2;
+		private SkinnedLabel skinnedLabel7;
+		private SkinnedComboBox skinnedComboBox2;
+		private System.Windows.Forms.ToolStripDropDownButton toolStripDropDownButton1;
+		private System.Windows.Forms.ToolStripMenuItem item1ToolStripMenuItem;
+		private System.Windows.Forms.ToolStripMenuItem item2ToolStripMenuItem;
+		private System.Windows.Forms.StatusStrip statusStrip1;
+		private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1;
+		private SkinnedButton cmdDisable;
+		private System.Windows.Forms.Panel panel1;
+		private SkinnedDataGridView skinnedDataGridView1;
+		private System.Windows.Forms.DataGridViewTextBoxColumn Column5;
+		private Desktop.Skinning.SkinnedDataGridViewComboBoxColumn Column6;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn Column7;
+		private Desktop.Skinning.SkinnedDataGridViewCheckBoxColumn Column8;
+		private SkinnedListView skinnedListView1;
+		private System.Windows.Forms.ColumnHeader columnHeader1;
+		private System.Windows.Forms.ColumnHeader columnHeader2;
+		private System.Windows.Forms.ToolStripButton toolStripButton3;
+		private System.Windows.Forms.ToolStrip toolStrip4;
+		private System.Windows.Forms.ToolStripButton toolStripButton10;
+		private System.Windows.Forms.ToolStripButton toolStripButton11;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator7;
+		private System.Windows.Forms.ToolStripSplitButton toolStripSplitButton4;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem11;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem12;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator8;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem13;
+		private System.Windows.Forms.ToolStripDropDownButton toolStripDropDownButton4;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem14;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem15;
+		private System.Windows.Forms.ToolStripButton toolStripButton12;
+		private System.Windows.Forms.ToolStrip toolStrip3;
+		private System.Windows.Forms.ToolStripButton toolStripButton7;
+		private System.Windows.Forms.ToolStripButton toolStripButton8;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator5;
+		private System.Windows.Forms.ToolStripSplitButton toolStripSplitButton3;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem6;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem7;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator6;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem8;
+		private System.Windows.Forms.ToolStripDropDownButton toolStripDropDownButton3;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem9;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem10;
+		private System.Windows.Forms.ToolStripButton toolStripButton9;
+		private System.Windows.Forms.ToolStrip toolStrip2;
+		private System.Windows.Forms.ToolStripButton toolStripButton4;
+		private System.Windows.Forms.ToolStripButton toolStripButton5;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
+		private System.Windows.Forms.ToolStripSplitButton toolStripSplitButton2;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem2;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem3;
+		private System.Windows.Forms.ToolStripDropDownButton toolStripDropDownButton2;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem4;
+		private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem5;
+		private System.Windows.Forms.ToolStripButton toolStripButton6;
+		private SkinnedButton skinnedButton4;
+		private SkinnedButton skinnedButton3;
+		private SkinnedButton skinnedButton2;
+		private SkinnedButton skinnedButton5;
+		private SkinnedButton skinnedButton6;
+		private SkinnedButton skinnedButton7;
+		private SkinnedGroupBox skinnedGroupBox2;
+		private SkinnedComboBox skinnedComboBox3;
+		private SkinnedComboBox skinnedComboBox5;
+		private SkinnedComboBox skinnedComboBox4;
+		private SkinnedComboBox skinnedComboBox6;
+		private SkinnedComboBox skinnedComboBox7;
+		private SkinnedComboBox skinnedComboBox8;
+		private SkinnedPanel skinnedPanel1;
+		private SkinnedButton skinnedButton8;
+		private SkinnedButton skinnedButton9;
+		private SkinnedButton skinnedButton10;
+		private CommonControls.AccordionListView accordionListView1;
+		private SkinnedSlider skinnedSlider2;
+		private SkinnedSlider skinnedSlider1;
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinTester.cs b/editor source/Desktop/Skinning/SkinTester.cs
new file mode 100644
index 0000000000000000000000000000000000000000..98e52ed26db56437c7c05b5ee261ce00823ab27b
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinTester.cs	
@@ -0,0 +1,84 @@
+using Desktop.CommonControls;
+using System.Windows.Forms;
+using System;
+using System.ComponentModel;
+
+namespace Desktop.Skinning
+{
+	public partial class SkinTester : UserControl
+	{
+		public SkinTester()
+		{
+			InitializeComponent();
+
+			skinnedComboBox1.Items.Add("Item 1");
+			skinnedComboBox1.Items.Add("Item 2");
+			skinnedComboBox3.Items.Add("Item 1");
+			skinnedComboBox4.Items.Add("Item 1");
+			skinnedComboBox5.Items.Add("Item 1");
+
+			skinnedComboBox2.Items.Add("Item 1");
+			skinnedComboBox2.Items.Add("Item 2");
+			skinnedComboBox2.AutoCompleteSource = AutoCompleteSource.ListItems;
+			skinnedComboBox2.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
+
+			skinnedComboBox1.SelectedIndex = 0;
+			skinnedComboBox2.SelectedIndex = 0;
+			skinnedComboBox3.SelectedIndex = 0;
+			skinnedComboBox4.SelectedIndex = 0;
+			skinnedComboBox5.SelectedIndex = 0;
+
+			skinnedDataGridView1.Rows.Add(new object[] { "Cell 1", "Cell 2", "Cell 3", true });
+		}
+
+		protected override void OnCreateControl()
+		{
+			base.OnCreateControl();
+			if (DesignMode) { return; }
+
+
+			AccordionColumn column = new AccordionColumn("Name", "Name");
+			column.FillWeight = 1;
+			accordionListView1.AddColumn(column);
+			column = new AccordionColumn("Number", "#");
+			column.Width = 60;
+			column.TextAlign = HorizontalAlignment.Right;
+			accordionListView1.AddColumn(column);
+			accordionListView1.RebuildColumns();
+
+			GroupedList<TestObject> accordionGroup = new GroupedList<TestObject>();
+			accordionGroup.AddItem(new TestObject("Apple", 5));
+			accordionGroup.AddItem(new TestObject("Ants", 23));
+			accordionGroup.AddItem(new TestObject("Banana", 2));
+			accordionListView1.DataSource = accordionGroup;
+		}
+
+		private void cmdDisable_Click(object sender, System.EventArgs e)
+		{
+			panel1.Enabled = !panel1.Enabled;
+		}
+
+		private class TestObject : IGroupedItem, INotifyPropertyChanged
+		{
+			public string Name { get; set; }
+			public int Number { get; set; }
+
+			public TestObject(string name, int number)
+			{
+				Name = name;
+				Number = number;
+			}
+
+			public event PropertyChangedEventHandler PropertyChanged
+			{
+				add { }
+				remove { }
+			}
+
+			public string GetGroupKey()
+			{
+				return string.IsNullOrEmpty(Name) ? "Default" : Name[0].ToString();
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinTester.resx b/editor source/Desktop/Skinning/SkinTester.resx
new file mode 100644
index 0000000000000000000000000000000000000000..69997ffe165df9e545e1b39bea4f425e337f5e8d
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinTester.resx	
@@ -0,0 +1,391 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>353, 17</value>
+  </metadata>
+  <metadata name="toolStrip4.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>679, 17</value>
+  </metadata>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="toolStripButton11.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripSplitButton4.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripDropDownButton4.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripButton12.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <metadata name="toolStrip3.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>574, 17</value>
+  </metadata>
+  <data name="toolStripButton8.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripSplitButton3.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripDropDownButton3.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripButton9.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <metadata name="toolStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>469, 17</value>
+  </metadata>
+  <data name="toolStripButton5.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripSplitButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripDropDownButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripButton6.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <metadata name="Column5.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="Column6.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="Column7.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="Column8.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>248, 17</value>
+  </metadata>
+  <data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripSplitButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripDropDownButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="toolStripButton3.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+</root>
\ No newline at end of file
diff --git a/editor source/Desktop/Skinning/SkinnedButton.cs b/editor source/Desktop/Skinning/SkinnedButton.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0410c483c8555cc2681e28a597546a631a0d6176
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedButton.cs	
@@ -0,0 +1,295 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedButton : Button, ISkinControl
+	{
+		public VisualState MouseState { get; private set; }
+
+		private bool _flat = false;
+		public bool Flat
+		{
+			get { return _flat; }
+			set { _flat = value; _colorSet = null; Invalidate(); }
+		}
+
+		private SkinnedFieldType _fieldType = SkinnedFieldType.Primary;
+		public SkinnedFieldType FieldType
+		{
+			get { return _fieldType; }
+			set { _fieldType = value; _colorSet = null; Invalidate(); }
+		}
+		private ColorSet _colorSet;
+
+		private SkinnedBackgroundType _background = SkinnedBackgroundType.Surface;
+		public SkinnedBackgroundType Background
+		{
+			get { return _background; }
+			set
+			{
+				_background = value;
+				Invalidate(true);
+			}
+		}
+
+		private Animator _animator = new Animator(0.15f);
+
+		protected override void OnCreateControl()
+		{
+			base.OnCreateControl();
+			if (DesignMode) { return; }
+
+			MouseEnter += SkinnedButton_MouseEnter;
+			MouseLeave += SkinnedButton_MouseLeave;
+			MouseDown += SkinnedButton_MouseDown;
+			MouseUp += SkinnedButton_MouseUp;
+			KeyDown += SkinnedButton_KeyDown;
+			KeyUp += SkinnedButton_KeyUp;
+			_animator.OnUpdate += _animator_OnUpdate;
+		}
+
+		private void _animator_OnUpdate(object sender, float e)
+		{
+			Invalidate();
+		}
+
+		private void SkinnedButton_MouseEnter(object sender, EventArgs e)
+		{
+			MouseState = VisualState.Hover;
+			_animator.StartAnimation(AnimationDirection.In);
+			Invalidate();
+		}
+
+		private void SkinnedButton_MouseLeave(object sender, EventArgs e)
+		{
+			MouseState = VisualState.Normal;
+			_animator.StartAnimation(AnimationDirection.Out);
+			Invalidate();
+		}
+
+		private void SkinnedButton_MouseDown(object sender, MouseEventArgs e)
+		{
+			if (e.Button == MouseButtons.Left)
+			{
+				MouseState = VisualState.Pressed;
+				Invalidate();
+			}
+		}
+
+		private void SkinnedButton_MouseUp(object sender, MouseEventArgs e)
+		{
+			MouseState = VisualState.Hover;
+			Invalidate();
+		}
+
+		private void SkinnedButton_KeyDown(object sender, KeyEventArgs e)
+		{
+			if (e.KeyCode == Keys.Space)
+			{
+				MouseState = VisualState.Pressed;
+				Invalidate();
+			}
+		}
+
+		private void SkinnedButton_KeyUp(object sender, KeyEventArgs e)
+		{
+			if (e.KeyCode == Keys.Space)
+			{
+				MouseState = VisualState.Normal;
+				Invalidate();
+			}
+		}
+
+		protected override void OnPaint(PaintEventArgs pevent)
+		{
+			base.OnPaint(pevent);
+			if (Flat)
+			{
+				DrawFlat(pevent);
+			}
+			else
+			{
+				DrawButton(pevent);
+			}
+		}
+
+		private void DrawFlat(PaintEventArgs pevent)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			if (_colorSet == null)
+			{
+				GetColorSet();
+			}
+
+			Graphics g = pevent.Graphics;
+			Color backColor = DesignMode ? SystemColors.Control : this.GetSkinnedPanelBackColor();
+			Color hoverColor = MouseState == VisualState.Pressed ? _colorSet.Pressed : _colorSet.Hover;
+			backColor = ColorSet.BlendColor(backColor, hoverColor, _animator.Value);
+
+			g.Clear(backColor);
+
+			Color foreColor = Enabled ? ForeColor : _colorSet.DisabledForeColor;
+			Color hoverForeColor = Enabled ? _colorSet.ForeColor : _colorSet.DisabledForeColor;
+			foreColor = ColorSet.BlendColor(foreColor, hoverForeColor, _animator.Value);
+
+			DrawContent(g, ClientRectangle, foreColor, Image, ImageAlign, Text, TextAlign);
+		}
+
+		private void DrawButton(PaintEventArgs pevent)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			if (_colorSet == null)
+			{
+				GetColorSet();
+			}
+
+			Graphics g = pevent.Graphics;
+
+			Color backColor = _colorSet.GetColor(VisualState.Normal, Focused, Enabled);
+			Color hoverColor = MouseState == VisualState.Pressed ? _colorSet.Pressed : _colorSet.Hover;
+			backColor = ColorSet.BlendColor(backColor, hoverColor, _animator.Value);
+
+			g.Clear(backColor);
+			if (Focused)
+			{
+				SkinManager.Instance.DrawFocusRectangle(g, pevent.ClipRectangle);
+			}
+			DrawContent(g, ClientRectangle, Enabled ? _colorSet.ForeColor : _colorSet.DisabledForeColor, Image, ImageAlign, Text, TextAlign);
+
+			//border
+			Pen borderPen = _colorSet.GetBorderPen(MouseState, Focused, Enabled);
+			g.DrawRectangle(borderPen, ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width - 1, ClientRectangle.Height - 1);
+		}
+
+		public static void DrawContent(Graphics g, Rectangle bounds, Color color, Image image, ContentAlignment imageAlign, string text, ContentAlignment textAlign)
+		{
+			using (Brush foreColor = new SolidBrush(color))
+			{
+				const int InnerPadding = 5;
+				Rectangle textRect = new Rectangle(bounds.X + InnerPadding, bounds.Y + InnerPadding, bounds.Width - InnerPadding * 2, bounds.Height - InnerPadding * 2);
+
+				if (image != null)
+				{
+					Rectangle imageRect = bounds;
+					switch (imageAlign)
+					{
+						case ContentAlignment.BottomCenter:
+							imageRect = new Rectangle(bounds.Width / 2 - image.Width / 2, bounds.Height - InnerPadding - image.Height, image.Width, image.Height);
+							break;
+						case ContentAlignment.BottomLeft:
+							imageRect = new Rectangle(InnerPadding, bounds.Height - InnerPadding - image.Height, image.Width, image.Height);
+							break;
+						case ContentAlignment.BottomRight:
+							imageRect = new Rectangle(bounds.Width - InnerPadding - image.Width, bounds.Height - InnerPadding - image.Height, image.Width, image.Height);
+							break;
+						case ContentAlignment.MiddleCenter:
+							imageRect = new Rectangle(bounds.Width / 2 - image.Width / 2, bounds.Height / 2 - image.Height / 2, image.Width, image.Height);
+							break;
+						case ContentAlignment.MiddleLeft:
+							imageRect = new Rectangle(InnerPadding, bounds.Height / 2 - image.Height / 2, image.Width, image.Height);
+							break;
+						case ContentAlignment.MiddleRight:
+							imageRect = new Rectangle(bounds.Width - InnerPadding - image.Width, bounds.Height / 2 - image.Height / 2, image.Width, image.Height);
+							break;
+						case ContentAlignment.TopCenter:
+							imageRect = new Rectangle(bounds.Width / 2 - image.Width / 2, InnerPadding, image.Width, image.Height);
+							break;
+						case ContentAlignment.TopLeft:
+							imageRect = new Rectangle(InnerPadding, InnerPadding, image.Width, image.Height);
+							break;
+						case ContentAlignment.TopRight:
+							imageRect = new Rectangle(bounds.Width - InnerPadding - image.Width, InnerPadding, image.Width, image.Height);
+							break;
+					}
+					g.DrawImage(image, imageRect);
+				}
+
+				StringFormat sf = new StringFormat();
+				sf.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show;
+				switch (textAlign)
+				{
+					case ContentAlignment.BottomCenter:
+						sf.LineAlignment = StringAlignment.Far;
+						sf.Alignment = StringAlignment.Center;
+						break;
+					case ContentAlignment.BottomLeft:
+						sf.LineAlignment = StringAlignment.Far;
+						sf.Alignment = StringAlignment.Near;
+						break;
+					case ContentAlignment.BottomRight:
+						sf.LineAlignment = StringAlignment.Far;
+						sf.Alignment = StringAlignment.Far;
+						break;
+					case ContentAlignment.MiddleCenter:
+						sf.LineAlignment = StringAlignment.Center;
+						sf.Alignment = StringAlignment.Center;
+						break;
+					case ContentAlignment.MiddleLeft:
+						sf.LineAlignment = StringAlignment.Center;
+						sf.Alignment = StringAlignment.Near;
+						break;
+					case ContentAlignment.MiddleRight:
+						sf.LineAlignment = StringAlignment.Center;
+						sf.Alignment = StringAlignment.Far;
+						break;
+					case ContentAlignment.TopCenter:
+						sf.LineAlignment = StringAlignment.Near;
+						sf.Alignment = StringAlignment.Center;
+						break;
+					case ContentAlignment.TopLeft:
+						sf.LineAlignment = StringAlignment.Near;
+						sf.Alignment = StringAlignment.Near;
+						break;
+					case ContentAlignment.TopRight:
+						sf.LineAlignment = StringAlignment.Near;
+						sf.Alignment = StringAlignment.Far;
+						break;
+				}
+				sf.FormatFlags = StringFormatFlags.NoWrap;
+				g.DrawString(text?.ToUpper() ?? "", Skin.ButtonFont, foreColor, textRect, sf);
+			}
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			_colorSet = null;
+		}
+
+		private void GetColorSet()
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			if (Flat)
+			{
+				_colorSet = skin.GetFieldColorSet(FieldType, SkinnedLightLevel.Light);
+				ForeColor = skin.GetForeColor(FieldType);
+
+				//forecolor depends on parent background
+				if (Parent != null && Parent is ISkinnedPanel)
+				{
+					ISkinnedPanel panel = Parent as ISkinnedPanel;
+					switch (panel.PanelType)
+					{
+						case SkinnedBackgroundType.PrimaryDark:
+							ForeColor = FieldType == SkinnedFieldType.Secondary ? skin.SecondaryForeColor : skin.PrimaryDarkColor.ForeColor;
+							break;
+						case SkinnedBackgroundType.SecondaryDark:
+							ForeColor = FieldType == SkinnedFieldType.Secondary ? skin.SecondaryDarkColor.ForeColor : skin.PrimaryForeColor;
+							break;
+					}
+				}
+			}
+			else
+			{
+				_colorSet = skin.GetFieldColorSet(FieldType, SkinnedLightLevel.Normal);
+			}
+		}
+	}
+
+	public enum ColorMode
+	{
+		Primary,
+		Secondary
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedCheckBox.cs b/editor source/Desktop/Skinning/SkinnedCheckBox.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8e686e0d493bcba565a462756364c771fde8842b
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedCheckBox.cs	
@@ -0,0 +1,194 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedCheckBox : CheckBox, ISkinControl
+	{
+		public VisualState MouseState { get; private set; }
+		private static StringFormat _sf = new StringFormat() { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Near, FormatFlags = StringFormatFlags.NoWrap };
+
+		private SkinnedFieldType _fieldType = SkinnedFieldType.Primary;
+		public SkinnedFieldType FieldType
+		{
+			get { return _fieldType; }
+			set { _fieldType = value; Invalidate(); }
+		}
+
+		public SkinnedCheckBox()
+		{
+			if (DesignMode)
+			{
+				OnUpdateSkin(new Skin());
+			}
+		}
+
+		protected override void OnCreateControl()
+		{
+			base.OnCreateControl();
+			if (DesignMode) { return; }
+
+			MouseEnter += MouseEnterEvent;
+			MouseLeave += MouseLeaveEvent;
+			MouseDown += MouseDownEvent;
+			MouseUp += MouseUpEvent;
+		}
+
+		private void MouseEnterEvent(object sender, EventArgs e)
+		{
+			MouseState = VisualState.Hover;
+			Invalidate();
+		}
+
+		private void MouseLeaveEvent(object sender, EventArgs e)
+		{
+			MouseState = VisualState.Normal;
+			Invalidate();
+		}
+
+		private void MouseDownEvent(object sender, MouseEventArgs e)
+		{
+			if (e.Button == MouseButtons.Left)
+			{
+				MouseState = VisualState.Pressed;
+				Invalidate();
+			}
+		}
+
+		private void MouseUpEvent(object sender, MouseEventArgs e)
+		{
+			MouseState = VisualState.Hover;
+			Invalidate();
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			Invalidate();
+		}
+
+		public const int CheckBoxSize = 12;
+		public static readonly Point[] CheckmarkPoints = { new Point(1, 5), new Point(5, 9), new Point(11, 3) };
+
+		public static void RenderCheckbox(Graphics g, int x, int y, SkinnedFieldType fieldType, CheckState checkState, VisualState state, bool enabled)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			Rectangle rect = new Rectangle(x, y, CheckBoxSize, CheckBoxSize);
+
+			using (Brush boxColor = new SolidBrush(skin.FieldBackColor))
+			{
+				ColorSet set = skin.GetWidgetColorSet(fieldType);
+				g.FillRectangle(boxColor, rect);
+				Pen borderPen = set.GetBorderPen(state, false, enabled);
+				g.DrawRectangle(borderPen, rect);
+
+				Brush checkBrush = set.GetBrush(state, false, enabled);
+				if (checkState == CheckState.Indeterminate)
+				{
+					g.FillRectangle(checkBrush, rect.X + 3, rect.Y + 3, rect.Width - 5, rect.Height - 5);
+				}
+				else if (checkState == CheckState.Checked)
+				{
+					g.FillRectangle(checkBrush, rect.X + 1, rect.Y + 1, rect.Width - 1, rect.Height - 1);
+					using (Pen check = new Pen(enabled ? set.ForeColor : set.DisabledForeColor, 2))
+					{
+						for (int i = 0; i < CheckmarkPoints.Length - 1; i++)
+						{
+							Point pt0 = new Point(x + CheckmarkPoints[i].X, y + CheckmarkPoints[i].Y);
+							Point pt1 = new Point(x + CheckmarkPoints[i + 1].X, y + CheckmarkPoints[i + 1].Y);
+							g.DrawLine(check, pt0, pt1);
+						}
+					}
+
+				}
+			}
+		}
+
+		protected override void OnPaint(PaintEventArgs pevent)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
+			Graphics g = pevent.Graphics;
+
+			g.Clear(DesignMode ? SystemColors.Control : this.GetSkinnedPanelBackColor());
+
+			if (Appearance == Appearance.Normal)
+			{
+				using (Brush boxColor = new SolidBrush(skin.FieldBackColor))
+				{
+					int start = pevent.ClipRectangle.Y + pevent.ClipRectangle.Height / 2 - CheckBoxSize / 2 - 1;
+					RenderCheckbox(g, 0, start, FieldType, CheckState, MouseState, Enabled);
+				}
+
+				Color foreColor = DesignMode ? ForeColor : this.GetSkinnedPanelForeColor();
+				int left = 2 + CheckBoxSize;
+				Rectangle textRect = new Rectangle(left, 0, ClientRectangle.Width - left, ClientRectangle.Height - 2);
+				TextRenderer.DrawText(g, Text, Font, textRect, foreColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
+				if (Focused)
+				{
+					textRect = new Rectangle(left - 1, textRect.Y - 1, textRect.Width, textRect.Height + 3);
+					SkinManager.Instance.DrawFocusRectangle(g, textRect);
+				}
+			}
+			else
+			{
+				DrawButton(g, skin);
+			}
+		}
+
+		private void DrawButton(Graphics g, Skin skin)
+		{
+			ColorSet set = skin.GetFieldColorSet(FieldType, SkinnedLightLevel.Normal);
+
+			Rectangle bounds = ClientRectangle;
+
+			VisualState state = MouseState;
+			if (Checked)
+			{
+				//back
+				SolidBrush backColor = set.GetBrush(state, Focused, Enabled);
+				g.FillRectangle(backColor, bounds);
+
+				//text
+				SkinnedButton.DrawContent(g, bounds, set.ForeColor, Image, ImageAlign, Text, ContentAlignment.MiddleCenter);
+
+				if (Focused)
+				{
+					SkinManager.Instance.DrawFocusRectangle(g, bounds);
+				}
+
+				//border
+				Pen borderPen = set.GetBorderPen(state, Focused, Enabled);
+				g.DrawRectangle(borderPen, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1);
+			}
+			else
+			{
+				Color foreColor = skin.GetForeColor(FieldType);
+				Color backColor = Parent.BackColor;
+
+				//back
+				if (state == VisualState.Hover)
+				{
+					backColor = set.Hover;
+					foreColor = set.ForeColor;
+				}
+				else if (state == VisualState.Pressed)
+				{
+					backColor = set.Pressed;
+					foreColor = set.ForeColor;
+				}
+				using (SolidBrush br = new SolidBrush(backColor))
+				{
+					g.FillRectangle(br, bounds);
+				}
+				//text
+				SkinnedButton.DrawContent(g, bounds, foreColor, Image, ImageAlign, Text, ContentAlignment.MiddleCenter);
+
+				if (Focused)
+				{
+					SkinManager.Instance.DrawFocusRectangle(g, bounds);
+				}
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedComboBox.cs b/editor source/Desktop/Skinning/SkinnedComboBox.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f310e0c24287856dece408be167ebfa1f4aecfa6
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedComboBox.cs	
@@ -0,0 +1,1081 @@
+using Desktop.CommonControls;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Design;
+using System.Reflection;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	[DefaultEvent("SelectedIndexChanged")]
+	public class SkinnedComboBox : ListControl, ISkinControl, ISkinnedComboBox
+	{
+		private const int ButtonWidth = 18;
+		private const int ArrowRadius = 4;
+
+		private bool _selectAllDone;
+
+		public VisualState MouseState { get; private set; }
+		[Editor("System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version = 2.0.0.0, Culture = neutral, PublicKeyToken = b03f5f7f11d50a3a", typeof(UITypeEditor))]
+		public ObjectCollection Items { get; }
+
+		private ToolStripControlHost _controlHost;
+		private SkinnedListBox _listBox;
+		private ToolStripDropDown _popupControl;
+		private SkinnedTextBox _textBox;
+		private bool _changingDataSource;
+
+		private Timer _typingTimer;
+		private string _typingString = "";
+		
+		private bool _isDroppedDown = false;
+		public bool DroppedDown { get { return _isDroppedDown; } }
+
+		private SkinnedFieldType _fieldType = SkinnedFieldType.Primary;
+		public SkinnedFieldType FieldType
+		{
+			get { return _fieldType; }
+			set { _fieldType = value; OnUpdateSkin(SkinManager.Instance.CurrentSkin); Invalidate(); }
+		}
+
+		private AutoCompleteSource _autoCompleteSource;
+		public AutoCompleteSource AutoCompleteSource
+		{
+			get { return _textBox.AutoCompleteSource; }
+			set
+			{
+				if (value == AutoCompleteSource.ListItems)
+				{
+					_autoCompleteSource = value;
+					value = AutoCompleteSource.CustomSource;
+					PopulateAutoCompleteFromList();
+				}
+				else
+				{
+					_autoCompleteSource = value;
+				}
+				_textBox.AutoCompleteSource = value;
+			}
+		}
+
+		public new object DataSource
+		{
+			get { return base.DataSource; }
+			set
+			{
+				_changingDataSource = true;
+				int index = _selectedIndex;
+				Items.Clear();
+				IEnumerable list = value as IEnumerable;
+				Items.AddRange(list);
+				_changingDataSource = false;
+				SelectedIndex = index;
+			}
+		}
+
+		public AutoCompleteMode AutoCompleteMode
+		{
+			get { return _textBox.AutoCompleteMode; }
+			set { _textBox.AutoCompleteMode = value; }
+		}
+
+		public AutoCompleteStringCollection AutoCompleteCustomSource
+		{
+			get { return _textBox.AutoCompleteCustomSource; }
+			set { _textBox.AutoCompleteCustomSource = value; }
+		}
+
+		private ComboBoxStyle _dropDownStyle;
+		public ComboBoxStyle DropDownStyle
+		{
+			get { return _dropDownStyle; }
+			set
+			{
+				_dropDownStyle = value;
+				if (_dropDownStyle == ComboBoxStyle.DropDownList)
+				{
+					_textBox.Visible = false;
+				}
+				else
+				{
+					_textBox.Visible = true;
+				}
+			}
+		}
+
+		[Category("Behavior"), Description("Occurs when the SelectedIndex property changes.")]
+		public event EventHandler SelectedIndexChanged;
+
+		public SkinnedComboBox()
+		{
+			Items = new ObjectCollection(this);
+			Items.Sorted = Sorted;
+
+			SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+			SetStyle(ControlStyles.UserMouse, true);
+			SetStyle(ControlStyles.UserPaint, true);
+			SetStyle(ControlStyles.ContainerControl, true);
+			SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
+			SetStyle(ControlStyles.Selectable, true);
+			SetStyle(ControlStyles.ResizeRedraw, true);
+			SetStyle(ControlStyles.SupportsTransparentBackColor, true);
+
+			this.SuspendLayout();
+			_typingTimer = new Timer();
+			_typingTimer.Interval = 1000;
+			_typingTimer.Tick += _typingTimer_Tick;
+			_textBox = new SkinnedTextBox();
+			_textBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			_textBox.Location = new System.Drawing.Point(3, 4);
+			_textBox.Size = new System.Drawing.Size(60, 13);
+			_textBox.TabIndex = 0;
+			_textBox.WordWrap = false;
+			_textBox.Margin = new Padding(0);
+			_textBox.Padding = new Padding(0);
+			_textBox.TextAlign = HorizontalAlignment.Left;
+			_textBox.Resize += new EventHandler(_textBox_Resize);
+			_textBox.TextChanged += new EventHandler(_textBox_TextChanged);
+			_textBox.Validated += _textBox_Validated;
+			_textBox.GotFocus += TxtField_GotFocus;
+			_textBox.Enter += TxtField_Enter;
+			_textBox.MouseUp += TxtField_MouseUp;
+			_textBox.Leave += TxtField_Leave;
+			Controls.Add(_textBox);
+			ResumeLayout(false);
+
+			PositionControls();
+
+			CreateDropdown();
+		}
+
+		private void TxtField_GotFocus(object sender, EventArgs e)
+		{
+			if (MouseButtons == MouseButtons.None)
+			{
+				_textBox.SelectAll();
+				_selectAllDone = true;
+			}
+		}
+
+		private void TxtField_Enter(object sender, EventArgs e)
+		{
+			if (!string.IsNullOrEmpty(_textBox.Text))
+			{
+				_textBox.SelectionStart = 0;
+				_textBox.SelectionLength = _textBox.Text.Length;
+			}
+		}
+
+		private void TxtField_MouseUp(object sender, MouseEventArgs e)
+		{
+			if (!_selectAllDone && _textBox.SelectionLength == 0)
+			{
+				_selectAllDone = true;
+				_textBox.SelectAll();
+			}
+		}
+
+		private void TxtField_Leave(object sender, EventArgs e)
+		{
+			_selectAllDone = false;
+		}
+
+		public static string GetFormattedValue(object item, string displayMember)
+		{
+			string value = "";
+			if (item == null || string.IsNullOrEmpty(displayMember))
+			{
+				value = item?.ToString() ?? "";
+			}
+			else
+			{
+				MemberInfo mi = PropertyTypeInfo.GetMemberInfo(item.GetType(), displayMember);
+				if (mi != null)
+				{
+					value = mi.GetValue(item)?.ToString();
+				}
+				else
+				{
+					value = item.ToString();
+				}
+			}
+			return value;
+		}
+
+		private void _textBox_Validated(object sender, EventArgs e)
+		{
+			string text = _textBox.Text.ToLower();
+			for (int i = 0; i < Items.Count; i++)
+			{
+				object item = Items[i];
+				string value = GetFormattedValue(item, DisplayMember);
+				if (value.ToLower() == text)
+				{
+					SelectedIndex = i;
+					break;
+				}
+			}
+		}
+
+		protected override void OnResize(EventArgs e)
+		{
+			PositionControls();
+			base.OnResize(e);
+		}
+
+		private void PositionControls()
+		{
+			SuspendLayout();
+			_textBox.Top = 4;
+			_textBox.Left = 5;
+			Rectangle buttonRect = GetButtonRect(ClientRectangle);
+			_textBox.Width = buttonRect.Left - 2 - _textBox.Left;
+			ResumeLayout();
+		}
+
+		private static Rectangle GetButtonRect(Rectangle clientBounds)
+		{
+			return new Rectangle(clientBounds.Left + clientBounds.Width - ButtonWidth, clientBounds.Top, ButtonWidth, clientBounds.Height);
+		}
+
+		private static Rectangle GetTextRect(Rectangle clientBounds)
+		{
+			return new Rectangle(clientBounds.Left + 2, clientBounds.Top, clientBounds.Width - ButtonWidth - 3, clientBounds.Height);
+		}
+
+		public override Font Font
+		{
+			get
+			{
+				return base.Font;
+			}
+
+			set
+			{
+				base.Font = value;
+				_textBox.Font = value;
+				Invalidate(true);
+			}
+		}
+
+		public override string Text
+		{
+			get
+			{
+				return _textBox.Text;
+			}
+			set
+			{
+				if (_textBox.Text != value)
+				{
+					for (int i = 0; i < Items.Count; i++)
+					{
+						if (GetFormattedValue(Items[i], DisplayMember) == value)
+						{
+							SelectedIndex = i;
+							break;
+						}
+					}
+					_textBox.Text = value;
+					base.Text = value;
+					OnTextChanged(EventArgs.Empty);
+				}
+			}
+		}
+
+		private void _typingTimer_Tick(object sender, EventArgs e)
+		{
+			_typingString = "";
+			_typingTimer.Stop();
+		}
+
+		public object SelectedItem
+		{
+			get { return _selectedIndex >= 0 ? Items[_selectedIndex] : null; }
+			set
+			{
+				int index = Items.IndexOf(value);
+				SelectedIndex = index;
+			}
+		}
+
+		public new object SelectedValue
+		{
+			get
+			{
+				object item = SelectedItem;
+				if (item != null)
+				{
+					string value = GetFormattedValue(item, ValueMember);
+					return value;
+				}
+				return null;
+			}
+			set
+			{
+				string valueMember = GetFormattedValue(value, ValueMember);
+				for (int i = 0; i < Items.Count; i++)
+				{
+					string v = GetFormattedValue(Items[i], ValueMember);
+					if (v == valueMember)
+					{
+						SelectedIndex = i;
+						return;
+					}
+				}
+				Text = valueMember;
+			}
+		}
+
+		private int _selectedIndex = -1;
+		public override int SelectedIndex
+		{
+			get { return _selectedIndex; }
+			set
+			{
+				if (!_changingDataSource && _selectedIndex != value && value >= 0 && value < Items.Count)
+				{
+					UpdateIndex(value);
+					OnSelectedIndexChanged(EventArgs.Empty);
+				}
+			}
+		}
+
+		private void UpdateIndex(int value)
+		{
+			_selectedIndex = value;
+			Text = GetFormattedValue(SelectedItem, DisplayMember);
+			Invalidate(true);
+		}
+
+		protected override void OnSelectedIndexChanged(EventArgs e)
+		{
+			SelectedIndexChanged?.Invoke(this, e);
+		}
+
+		private bool _sorted;
+		public bool Sorted
+		{
+			get { return _sorted; }
+			set
+			{
+				_sorted = value;
+				if (Items != null)
+				{
+					Items.Sorted = value;
+				}
+			}
+		}
+
+		protected override void OnMouseWheel(MouseEventArgs e)
+		{
+			if (e.Delta < 0 && SelectedIndex < Items.Count - 1)
+			{
+				SelectedIndex++;
+			}
+			else if (e.Delta > 0 && SelectedIndex > 0)
+			{
+				SelectedIndex--;
+			}
+		}
+
+		protected override void OnControlAdded(ControlEventArgs e)
+		{
+			e.Control.MouseDown += new MouseEventHandler(Control_MouseDown);
+			e.Control.MouseEnter += new EventHandler(Control_MouseEnter);
+			e.Control.MouseLeave += new EventHandler(Control_MouseLeave);
+			e.Control.GotFocus += new EventHandler(Control_GotFocus);
+			e.Control.LostFocus += new EventHandler(Control_LostFocus);
+			base.OnControlAdded(e);
+		}
+		#region NestedControlsEvents
+		void Control_LostFocus(object sender, EventArgs e)
+		{
+			OnLostFocus(e);
+		}
+
+		void Control_GotFocus(object sender, EventArgs e)
+		{
+			OnGotFocus(e);
+		}
+
+		void Control_MouseLeave(object sender, EventArgs e)
+		{
+			OnMouseLeave(e);
+		}
+
+		void Control_MouseEnter(object sender, EventArgs e)
+		{
+			OnMouseEnter(e);
+		}
+
+		void Control_MouseDown(object sender, MouseEventArgs e)
+		{
+			OnMouseDown(e);
+		}
+		#endregion
+
+		protected override void OnMouseMove(MouseEventArgs e)
+		{
+			base.OnMouseMove(e);
+			if (DropDownStyle == ComboBoxStyle.DropDownList || GetButtonRect(ClientRectangle).Contains(new Point(e.X, e.Y)))
+			{
+				MouseState = VisualState.Hover;
+				Cursor = Cursors.Default;
+			}
+			else if (GetTextRect(ClientRectangle).Contains(new Point(e.X, e.Y)))
+			{
+				Cursor = Cursors.IBeam;
+			}
+			else
+			{
+				MouseState = VisualState.Normal;
+				Cursor = Cursors.Default;
+			}
+			Invalidate();
+		}
+
+		protected override void OnMouseLeave(EventArgs e)
+		{
+			MouseState = VisualState.Normal;
+			Invalidate();
+		}
+
+		protected override void OnMouseDown(MouseEventArgs e)
+		{
+			if (e.Button == MouseButtons.Left && (DropDownStyle == ComboBoxStyle.DropDownList || GetButtonRect(ClientRectangle).Contains(new Point(e.X, e.Y))))
+			{
+				Focus();
+				MouseState = VisualState.Pressed;
+				Invalidate();
+
+				if (_isDroppedDown)
+				{
+					HideDropDown();
+				}
+				else if (DropDownStyle == ComboBoxStyle.DropDownList || RectangleToScreen(GetButtonRect(ClientRectangle)).Contains(MousePosition))
+				{
+					ShowDropDown();
+				}
+			}
+		}
+
+		protected override void OnMouseUp(MouseEventArgs e)
+		{
+			MouseState = VisualState.Hover;
+			Invalidate();
+		}
+
+		protected override bool IsInputKey(Keys keyData)
+		{
+			switch (keyData)
+			{
+				case Keys.Right:
+				case Keys.Left:
+				case Keys.Up:
+				case Keys.Down:
+					return true;
+				case Keys.Escape:
+					return DroppedDown;
+				default:
+					return base.IsInputKey(keyData);
+			}
+		}
+
+		protected override void OnKeyDown(KeyEventArgs e)
+		{
+			base.OnKeyDown(e);
+			if (e.KeyCode == Keys.Space)
+			{
+				ShowDropDown();
+			}
+			else if (e.KeyCode == Keys.Down && SelectedIndex < Items.Count - 1)
+			{
+				SelectedIndex++;
+			}
+			else if (e.KeyCode == Keys.Up && SelectedIndex > 0)
+			{
+				SelectedIndex--;
+			}
+			else if (e.KeyCode == Keys.Escape)
+			{
+				HideDropDown();
+			}
+		}
+
+		protected override void OnGotFocus(EventArgs e)
+		{
+			base.OnGotFocus(e);
+			if (_textBox.Visible)
+			{
+				_textBox.Focus();
+			}
+			Invalidate(true);
+		}
+
+		protected override void OnLostFocus(EventArgs e)
+		{
+			base.OnLostFocus(e);
+			MouseState = VisualState.Normal;
+			Invalidate(true);
+		}
+
+		protected override void OnKeyPress(KeyPressEventArgs e)
+		{
+			if (DropDownStyle == ComboBoxStyle.DropDownList)
+			{
+				_typingTimer.Stop();
+				_typingTimer.Start();
+
+				char character = e.KeyChar;
+				_typingString += character;
+				_typingString = _typingString.ToLower();
+				bool found = false;
+				int start = SelectedIndex;
+				if (start < 0)
+				{
+					start = 0;
+				}
+				for (int i = start + 1; i < Items.Count; i++)
+				{
+					string value = GetFormattedValue(Items[i], DisplayMember);
+					if (value == null)
+					{
+						continue;
+					}
+					if (value.ToLower().StartsWith(_typingString))
+					{
+						found = true;
+						SelectedIndex = i;
+						break;
+					}
+				}
+				if (!found)
+				{
+					for (int i = 0; i < start; i++)
+					{
+						string value = GetFormattedValue(Items[i], DisplayMember);
+						if (value == null)
+						{
+							continue;
+						}
+						if (value.StartsWith(_typingString))
+						{
+							found = true;
+							SelectedIndex = i;
+							break;
+						}
+					}
+				}
+				if (found)
+				{
+					e.Handled = true;
+				}
+			}
+			base.OnKeyPress(e);
+		}
+
+		private void CreateDropdown()
+		{
+			_listBox = new SkinnedListBox();
+			_listBox.IntegralHeight = false;
+			_listBox.BorderStyle = BorderStyle.FixedSingle;
+			_listBox.SelectionMode = SelectionMode.One;
+			_listBox.BindingContext = new BindingContext();
+			_listBox.FormattingEnabled = true;
+
+			_controlHost = new ToolStripControlHost(_listBox);
+			_controlHost.Padding = new Padding(0);
+			_controlHost.Margin = new Padding(0);
+			_controlHost.AutoSize = false;
+
+			_popupControl = new ToolStripDropDown();
+			_popupControl.Padding = new Padding(0);
+			_popupControl.Margin = new Padding(0);
+			_popupControl.AutoSize = true;
+			_popupControl.DropShadowEnabled = false;
+			_popupControl.Items.Add(_controlHost);
+			_popupControl.Closed += new ToolStripDropDownClosedEventHandler(_popupControl_Closed);
+
+			_listBox.MouseClick += new MouseEventHandler(_listBox_MouseClick);
+			_listBox.MouseMove += new MouseEventHandler(_listBox_MouseMove);
+			_listBox.KeyDown += _listBox_KeyDown;
+		}
+
+		#region ListBox events
+		private void _listBox_KeyDown(object sender, KeyEventArgs e)
+		{
+			if (e.KeyCode == Keys.Space || e.KeyCode == Keys.Enter)
+			{
+				SelectedIndex = _listBox.SelectedIndex;
+
+				if (DropDownStyle == ComboBoxStyle.DropDownList)
+				{
+					Invalidate(true);
+				}
+
+				HideDropDown();
+			}
+		}
+
+		private void _listBox_MouseMove(object sender, MouseEventArgs e)
+		{
+			int i;
+			for (i = 0; i < (_listBox.Items.Count); i++)
+			{
+				if (_listBox.GetItemRectangle(i).Contains(_listBox.PointToClient(MousePosition)))
+				{
+					_listBox.SelectedIndex = i;
+					return;
+				}
+			}
+		}
+
+		private void _listBox_MouseClick(object sender, MouseEventArgs e)
+		{
+			if (_listBox.Items.Count == 0)
+			{
+				return;
+			}
+
+			if (_listBox.SelectedItems.Count != 1)
+			{
+				return;
+			}
+
+			this.SelectedIndex = _listBox.SelectedIndex;
+
+			if (DropDownStyle == ComboBoxStyle.DropDownList)
+			{
+				this.Invalidate(true);
+			}
+
+			HideDropDown();
+		}
+
+		void _popupControl_Closed(object sender, ToolStripDropDownClosedEventArgs e)
+		{
+			_isDroppedDown = false;
+			if (!this.RectangleToScreen(this.ClientRectangle).Contains(MousePosition))
+			{
+				MouseState = VisualState.Normal;
+			}
+			Invalidate(true);
+		}
+
+		private void HideDropDown()
+		{
+			if (_isDroppedDown && _popupControl.IsDropDown)
+			{
+				_popupControl.Close();
+			}
+			_isDroppedDown = false;
+		}
+
+		public void ShowDropDown()
+		{
+			HideDropDown();
+
+			_isDroppedDown = true;
+
+			_controlHost.Control.Width = Width;
+
+			_listBox.Items.Clear();
+			foreach (object item in Items)
+			{
+				_listBox.Items.Add(item);
+			}
+			_listBox.SelectedIndex = SelectedIndex;
+			_listBox.DisplayMember = DisplayMember;
+			_listBox.Refresh();
+
+			const int DropDownItemHeight = 13;
+
+			if (Items.Count > 0)
+			{
+				_listBox.Height = Math.Min(500, DropDownItemHeight * Items.Count + 2);
+			}
+			else
+			{
+				_listBox.Height = 26;
+			}
+
+			_popupControl.Show(this, CalculateDropPosition(), ToolStripDropDownDirection.BelowRight);
+			_listBox.Focus();
+
+			Invalidate(true);
+		}
+
+		private Point CalculateDropPosition()
+		{
+			Point point = new Point(0, this.Height);
+			if ((this.PointToScreen(new Point(0, 0)).Y + this.Height + _controlHost.Height) > Screen.PrimaryScreen.WorkingArea.Height)
+			{
+				point.Y = -this._controlHost.Height - 7;
+			}
+			return point;
+		}
+
+		void _textBox_Resize(object sender, EventArgs e)
+		{
+			PositionControls();
+		}
+
+		void _textBox_TextChanged(object sender, EventArgs e)
+		{
+			OnTextChanged(e);
+		}
+		#endregion
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			Font = Skin.TextFont;
+			BackColor = skin.FieldBackColor;
+			_listBox.OnUpdateSkin(skin);
+		}
+
+		private void PopulateAutoCompleteFromList()
+		{
+			AutoCompleteStringCollection col = new AutoCompleteStringCollection();
+			foreach (object item in Items)
+			{
+				string value = GetFormattedValue(item, DisplayMember);
+				col.Add(value);
+			}
+			_textBox.AutoCompleteCustomSource = col;
+		}
+
+		private object _itemDuringChange;
+		public void PreItemChange()
+		{
+			_itemDuringChange = SelectedItem;
+		}
+
+		public void PostItemChange()
+		{
+			int index = Items.IndexOf(_itemDuringChange);
+			if (string.IsNullOrEmpty(Text) || index >= 0 || DropDownStyle == ComboBoxStyle.DropDownList)
+			{
+				//only change if the text was something that isn't part of the list
+				UpdateIndex(index);
+			}
+			_itemDuringChange = null;
+
+			if (AutoCompleteSource == AutoCompleteSource.ListItems)
+			{
+				PopulateAutoCompleteFromList();
+			}
+		}
+
+		public void SortItems()
+		{
+			object item = SelectedItem;
+			Items.SortItems();
+			if (item != null)
+			{
+				UpdateIndex(Items.IndexOf(item));
+			}
+		}
+
+		protected override void OnPaint(PaintEventArgs e)
+		{
+			base.OnPaint(e);
+			Rectangle bounds = ClientRectangle;
+
+			bool textBoxFocused = _textBox.Focused;
+			bool focused = Focused;
+			bool enabled = Enabled;
+			ComboBoxStyle style = DropDownStyle;
+			Graphics g = e.Graphics;
+			string value = GetFormattedValue(SelectedItem, DisplayMember);
+			RenderComboBox(g, bounds, value, textBoxFocused, focused, enabled, style, FieldType, MouseState);
+		}
+
+		public static void RenderComboBox(Graphics g, Rectangle bounds, object value, bool textBoxFocused, bool focused, bool enabled, ComboBoxStyle style, SkinnedFieldType fieldType, VisualState state)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			Rectangle buttonRect = GetButtonRect(bounds);
+			Rectangle textRect = GetTextRect(bounds);
+
+			ColorSet colorSet;
+			if (style == ComboBoxStyle.DropDownList)
+			{
+				colorSet = skin.GetFieldColorSet(fieldType, SkinnedLightLevel.Light);
+			}
+			else
+			{
+				colorSet = skin.GetFieldColorSet(fieldType, SkinnedLightLevel.Normal);
+			}
+
+			// field
+			Color backColor = enabled ? skin.FieldBackColor : skin.FieldDisabledBackColor;
+			if (style == ComboBoxStyle.DropDownList)
+			{
+				backColor = colorSet.GetColor(state, focused, enabled);
+			}
+			using (SolidBrush back = new SolidBrush(backColor))
+			{
+				g.FillRectangle(back, bounds);
+			}
+
+			// button background
+			if (style != ComboBoxStyle.DropDownList)
+			{
+				Brush buttonBrush = colorSet.GetBrush(state, focused, enabled);
+				g.FillRectangle(buttonBrush, buttonRect);
+			}
+
+			Pen pen = colorSet.GetBorderPen(state, focused, enabled);
+
+			// button foreground
+			Color foreColor = enabled ? colorSet.ForeColor : colorSet.DisabledForeColor;
+			if (style == ComboBoxStyle.DropDownList)
+			{
+				g.DrawLine(pen, bounds.Right - ButtonWidth, bounds.Y + 1, bounds.Right - ButtonWidth, bounds.Bottom - 2);
+			}
+			else
+			{
+				g.DrawLine(pen, bounds.Right - ButtonWidth, bounds.Y, bounds.Right - ButtonWidth, bounds.Bottom);
+			}
+			using (Pen arrowPen = new Pen(foreColor, 2))
+			{
+				g.DrawLine(arrowPen, bounds.Right - ButtonWidth / 2 - ArrowRadius, bounds.Y + bounds.Height / 2 - ArrowRadius / 2, bounds.Right - ButtonWidth / 2, bounds.Y + bounds.Height / 2 + ArrowRadius / 2);
+				g.DrawLine(arrowPen, bounds.Right - ButtonWidth / 2, bounds.Y + bounds.Height / 2 + ArrowRadius / 2, bounds.Right - ButtonWidth / 2 + ArrowRadius, bounds.Y + bounds.Height / 2 - ArrowRadius / 2);
+			}
+
+			// text
+			if (style == ComboBoxStyle.DropDownList)
+			{
+				using (Brush br = new SolidBrush(foreColor))
+				{
+					if (value != null)
+					{
+						using (StringFormat sf = new StringFormat() { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center, FormatFlags = StringFormatFlags.NoWrap, Trimming = StringTrimming.EllipsisCharacter })
+						{
+							g.DrawString(value.ToString(), Skin.TextFont, br, textRect, sf);
+						}
+					}
+				}
+
+			}
+			// border
+			if (focused || textBoxFocused)
+			{
+				SkinManager.Instance.DrawFocusRectangle(g, new Rectangle(bounds.X, bounds.Y, bounds.Width - ButtonWidth + 1, bounds.Height));
+			}
+			g.DrawRectangle(pen, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1);
+		}
+
+		public void SelectAll()
+		{
+			_textBox.SelectAll();
+		}
+
+		protected override void RefreshItem(int index)
+		{
+			//throw new NotImplementedException();
+		}
+
+		protected override void SetItemsCore(IList items)
+		{
+			//throw new NotImplementedException();
+		}
+
+		public class ObjectCollection : IList
+		{
+			private List<object> _objects = new List<object>();
+
+			private ISkinnedComboBox _owner;
+
+			private bool _bulkAdd;
+
+			private bool _sorted;
+			public bool Sorted
+			{
+				get { return _sorted; }
+				set
+				{
+					if (_sorted != value)
+					{
+						_sorted = value;
+						if (_sorted)
+						{
+							_owner.PreItemChange();
+							_owner.SortItems();
+							_owner.PostItemChange();
+						}
+					}
+				}
+			}
+
+			public string DisplayMember { get; set; }
+
+			public ObjectCollection(ISkinnedComboBox owner)
+			{
+				_owner = owner;
+			}
+
+			public int Count
+			{
+				get { return _objects.Count; }
+			}
+
+			public bool IsReadOnly
+			{
+				get
+				{
+					return false;
+				}
+			}
+
+			public bool IsFixedSize
+			{
+				get
+				{
+					return false;
+				}
+			}
+
+			public object SyncRoot
+			{
+				get
+				{
+					return false;
+				}
+			}
+
+			public bool IsSynchronized
+			{
+				get
+				{
+					return false;
+				}
+			}
+
+			internal void SortItems()
+			{
+				_objects.Sort(Sort);
+			}
+
+			private int Sort(object o1, object o2)
+			{
+				string v1 = GetFormattedValue(o1, _owner.DisplayMember);
+				string v2 = GetFormattedValue(o2, _owner.DisplayMember);
+				return v1.CompareTo(v2);
+			}
+
+			public object this[int index]
+			{
+				get
+				{
+					if (index < 0 || index >= _objects.Count)
+					{
+						return null;
+					}
+					return _objects[index];
+				}
+
+				set
+				{
+					_objects[index] = value;
+				}
+			}
+
+			public int Add(object value)
+			{
+				if (!_bulkAdd)
+				{
+					_owner.PreItemChange();
+				}
+				_objects.Add(value);
+				if (!_bulkAdd)
+				{
+					if (Sorted)
+					{
+						_owner.SortItems();
+					}
+					_owner.PostItemChange();
+				}
+				return _objects.IndexOf(value);
+			}
+
+			public void AddRange(IEnumerable objects)
+			{
+				_bulkAdd = true;
+				_owner.PreItemChange();
+				foreach (object o in objects)
+				{
+					Add(o);
+				}
+				if (_sorted)
+				{
+					_owner.SortItems();
+				}
+				_bulkAdd = false;
+				_owner.PostItemChange();
+			}
+
+			public bool Contains(object value)
+			{
+				return _objects.Contains(value);
+			}
+
+			public void Clear()
+			{
+				_owner.PreItemChange();
+				_objects.Clear();
+				_owner.PostItemChange();
+			}
+
+			public int IndexOf(object value)
+			{
+				return _objects.IndexOf(value);
+			}
+
+			public void Insert(int index, object value)
+			{
+				_owner.PreItemChange();
+				_objects.Insert(index, value);
+				_owner.PostItemChange();
+			}
+
+			public void Remove(object value)
+			{
+				_owner.PreItemChange();
+				_objects.Remove(value);
+				_owner.PostItemChange();
+			}
+
+			public void RemoveAt(int index)
+			{
+				_owner.PreItemChange();
+				_objects.RemoveAt(index);
+				_owner.PostItemChange();
+			}
+
+			public void CopyTo(Array array, int index)
+			{
+				for (int i = 0; i < _objects.Count; i++)
+				{
+					array.SetValue(_objects[i], index + i);
+				}
+			}
+
+			public IEnumerator GetEnumerator()
+			{
+				return _objects.GetEnumerator();
+			}
+		}
+	}
+
+	public interface ISkinnedComboBox
+	{
+		void PreItemChange();
+		void SortItems();
+		void PostItemChange();
+		string DisplayMember { get; }
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedDataGridView.cs b/editor source/Desktop/Skinning/SkinnedDataGridView.cs
new file mode 100644
index 0000000000000000000000000000000000000000..93653e28e236a45ebe6d83f7066c77f6555152c3
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedDataGridView.cs	
@@ -0,0 +1,60 @@
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedDataGridView : DataGridView, ISkinControl
+	{
+		const int HeaderPadding = 5;
+
+		public SkinnedDataGridView()
+		{
+			OnUpdateSkin(new Skin());
+			DoubleBuffered = true;
+			this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			if (skin != null)
+			{
+				if (Parent != null && Parent.BackColor != System.Drawing.Color.Transparent)
+				{
+					BackgroundColor = Parent.BackColor;
+				}
+				else
+				{
+					BackgroundColor = skin.Background.Normal;
+				}
+				
+				BorderStyle = BorderStyle.None;
+				GridColor = skin.PrimaryColor.Border;
+				EnableHeadersVisualStyles = false;
+
+				ColumnHeadersBorderStyle = RowHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single;
+
+				DefaultCellStyle.BackColor = skin.FieldBackColor;
+				Font = Skin.TextFont;
+				ColumnHeadersDefaultCellStyle.BackColor = RowHeadersDefaultCellStyle.BackColor = skin.Surface.Normal;
+				ColumnHeadersDefaultCellStyle.ForeColor = RowHeadersDefaultCellStyle.ForeColor = skin.Surface.ForeColor;
+				ColumnHeadersDefaultCellStyle.Padding = new Padding(0, HeaderPadding, 0, HeaderPadding);
+
+				foreach (DataGridViewColumn column in Columns)
+				{
+					column.DefaultCellStyle.BackColor = skin.FieldBackColor;
+					column.DefaultCellStyle.ForeColor = skin.Surface.ForeColor;
+				}
+
+				foreach (DataGridViewRow row in Rows)
+				{
+					foreach (DataGridViewCell cell in row.Cells)
+					{
+						cell.Style.BackColor = skin.FieldBackColor;
+						cell.Style.ForeColor = skin.Surface.ForeColor;
+					}
+				}
+
+				Invalidate(true);
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedForm.cs b/editor source/Desktop/Skinning/SkinnedForm.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0684d7087f1c720be95ad79dc611ec9650f170d0
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedForm.cs	
@@ -0,0 +1,679 @@
+// This file is a modified form of what was originally MaterialForm from the MaterialSkin project, licensed below.
+
+//The MIT License(MIT)
+
+//Copyright(c) 2014 Ignace Maes
+
+//Permission is hereby granted, free of charge, to any person obtaining a copy
+//of this software and associated documentation files (the "Software"), to deal
+//in the Software without restriction, including without limitation the rights
+//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//copies of the Software, and to permit persons to whom the Software is
+//furnished to do so, subject to the following conditions:
+
+//The above copyright notice and this permission notice shall be included in all
+//copies or substantial portions of the Software.
+
+//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//SOFTWARE.
+
+
+using Desktop.Messaging;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using static NativeMethods;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedForm : Form, ISkinnedPanel
+	{
+		public bool Sizable { get; set; }
+		public bool ShowTitleBar { get; set; } = true;
+
+		private Mailbox _mailbox;
+		private int _threadId;
+
+		#region Custom menu bar
+
+		public const int WM_NCLBUTTONDOWN = 0xA1;
+		public const int HT_CAPTION = 0x2;
+		public const int WM_MOUSEMOVE = 0x0200;
+		public const int WM_LBUTTONDOWN = 0x0201;
+		public const int WM_LBUTTONUP = 0x0202;
+		public const int WM_LBUTTONDBLCLK = 0x0203;
+		public const int WM_RBUTTONDOWN = 0x0204;
+		private const int HTBOTTOMLEFT = 16;
+		private const int HTBOTTOMRIGHT = 17;
+		private const int HTLEFT = 10;
+		private const int HTRIGHT = 11;
+		private const int HTBOTTOM = 15;
+		private const int HTTOP = 12;
+		private const int HTTOPLEFT = 13;
+		private const int HTTOPRIGHT = 14;
+		private const int BORDER_WIDTH = 7;
+		private ResizeDirection _resizeDir;
+		private ButtonState _buttonState = ButtonState.None;
+
+		private const int WMSZ_TOP = 3;
+		private const int WMSZ_TOPLEFT = 4;
+		private const int WMSZ_TOPRIGHT = 5;
+		private const int WMSZ_LEFT = 1;
+		private const int WMSZ_RIGHT = 2;
+		private const int WMSZ_BOTTOM = 6;
+		private const int WMSZ_BOTTOMLEFT = 7;
+		private const int WMSZ_BOTTOMRIGHT = 8;
+
+		private readonly Dictionary<int, int> _resizingLocationsToCmd = new Dictionary<int, int>
+		{
+			{HTTOP,         WMSZ_TOP},
+			{HTTOPLEFT,     WMSZ_TOPLEFT},
+			{HTTOPRIGHT,    WMSZ_TOPRIGHT},
+			{HTLEFT,        WMSZ_LEFT},
+			{HTRIGHT,       WMSZ_RIGHT},
+			{HTBOTTOM,      WMSZ_BOTTOM},
+			{HTBOTTOMLEFT,  WMSZ_BOTTOMLEFT},
+			{HTBOTTOMRIGHT, WMSZ_BOTTOMRIGHT}
+		};
+
+		private const int STATUS_BAR_BUTTON_WIDTH = StatusBarHeight;
+		private const int StatusBarHeight = 27;
+		private const int IconSize = 16;
+		private const int ACTION_BAR_HEIGHT = 40;
+
+		private const uint TPM_LEFTALIGN = 0x0000;
+		private const uint TPM_RETURNCMD = 0x0100;
+
+		private const int WM_SYSCOMMAND = 0x0112;
+		private const int WS_MINIMIZEBOX = 0x20000;
+		private const int WS_SYSMENU = 0x00080000;
+
+		private const int MONITOR_DEFAULTTONEAREST = 2;
+
+		private enum ResizeDirection
+		{
+			BottomLeft,
+			Left,
+			Right,
+			BottomRight,
+			Bottom,
+			Top,
+			TopLeft,
+			TopRight,
+			None
+		}
+
+		private enum ButtonState
+		{
+			XOver,
+			MaxOver,
+			MinOver,
+			XDown,
+			MaxDown,
+			MinDown,
+			None
+		}
+
+		private readonly Cursor[] _resizeCursors = { Cursors.SizeNESW, Cursors.SizeWE, Cursors.SizeNWSE, Cursors.SizeWE, Cursors.SizeNS };
+
+		private Rectangle _minButtonBounds;
+		private Rectangle _maxButtonBounds;
+		private Rectangle _xButtonBounds;
+		private Rectangle _actionBarBounds;
+		private Rectangle _statusBarBounds;
+
+		private bool _maximized;
+		private Size _previousSize;
+		private Point _previousLocation;
+		private bool _headerMouseDown;
+
+		protected override void WndProc(ref Message m)
+		{
+			base.WndProc(ref m);
+			if (DesignMode || IsDisposed) return;
+
+			if (m.Msg == WM_LBUTTONDBLCLK)
+			{
+				MaximizeWindow(!_maximized);
+			}
+			else if (m.Msg == WM_MOUSEMOVE && _maximized &&
+				(_statusBarBounds.Contains(PointToClient(Cursor.Position)) || _actionBarBounds.Contains(PointToClient(Cursor.Position))) &&
+				!(_minButtonBounds.Contains(PointToClient(Cursor.Position)) || _maxButtonBounds.Contains(PointToClient(Cursor.Position)) || _xButtonBounds.Contains(PointToClient(Cursor.Position))))
+			{
+				if (_headerMouseDown)
+				{
+					_maximized = false;
+					_headerMouseDown = false;
+
+					var mousePoint = PointToClient(Cursor.Position);
+					if (mousePoint.X < Width / 2)
+						Location = mousePoint.X < _previousSize.Width / 2 ?
+							new Point(Cursor.Position.X - mousePoint.X, Cursor.Position.Y - mousePoint.Y) :
+							new Point(Cursor.Position.X - _previousSize.Width / 2, Cursor.Position.Y - mousePoint.Y);
+					else
+						Location = Width - mousePoint.X < _previousSize.Width / 2 ?
+							new Point(Cursor.Position.X - _previousSize.Width + Width - mousePoint.X, Cursor.Position.Y - mousePoint.Y) :
+							new Point(Cursor.Position.X - _previousSize.Width / 2, Cursor.Position.Y - mousePoint.Y);
+
+					Size = _previousSize;
+					ReleaseCapture();
+					SendMessage(Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, (IntPtr)0);
+				}
+			}
+			else if (m.Msg == WM_LBUTTONDOWN &&
+				(_statusBarBounds.Contains(PointToClient(Cursor.Position)) || _actionBarBounds.Contains(PointToClient(Cursor.Position))) &&
+				!(_minButtonBounds.Contains(PointToClient(Cursor.Position)) || _maxButtonBounds.Contains(PointToClient(Cursor.Position)) || _xButtonBounds.Contains(PointToClient(Cursor.Position))))
+			{
+				if (!_maximized)
+				{
+					ReleaseCapture();
+					SendMessage(Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, (IntPtr)0);
+				}
+				else
+				{
+					_headerMouseDown = true;
+				}
+			}
+			else if (m.Msg == WM_RBUTTONDOWN)
+			{
+				Point cursorPos = PointToClient(Cursor.Position);
+
+				if (_statusBarBounds.Contains(cursorPos) && !_minButtonBounds.Contains(cursorPos) &&
+					!_maxButtonBounds.Contains(cursorPos) && !_xButtonBounds.Contains(cursorPos))
+				{
+					// Show default system menu when right clicking titlebar
+					var id = TrackPopupMenuEx(GetSystemMenu(Handle, false), TPM_LEFTALIGN | TPM_RETURNCMD, Cursor.Position.X, Cursor.Position.Y, Handle, IntPtr.Zero);
+
+					// Pass the command as a WM_SYSCOMMAND message
+					SendMessage(Handle, WM_SYSCOMMAND, (IntPtr)id, (IntPtr)0);
+				}
+			}
+			else if (m.Msg == WM_NCLBUTTONDOWN)
+			{
+				// This re-enables resizing by letting the application know when the
+				// user is trying to resize a side. This is disabled by default when using WS_SYSMENU.
+				if (!Sizable) return;
+
+				byte bFlag = 0;
+
+				// Get which side to resize from
+				if (_resizingLocationsToCmd.ContainsKey((int)m.WParam))
+				{
+					bFlag = (byte)_resizingLocationsToCmd[(int)m.WParam];
+				}
+
+				if (bFlag != 0)
+				{
+					SendMessage(Handle, WM_SYSCOMMAND, (IntPtr)(0xF000 | bFlag), m.LParam);
+				}
+			}
+			else if (m.Msg == WM_LBUTTONUP)
+			{
+				_headerMouseDown = false;
+			}
+		}
+
+		protected override CreateParams CreateParams
+		{
+			get
+			{
+				var par = base.CreateParams;
+				// WS_SYSMENU: Trigger the creation of the system menu
+				// WS_MINIMIZEBOX: Allow minimizing from taskbar
+				par.Style = par.Style | WS_MINIMIZEBOX | WS_SYSMENU; // Turn on the WS_MINIMIZEBOX style flag
+				return par;
+			}
+		}
+
+		public SkinnedBackgroundType PanelType
+		{
+			get { return SkinnedBackgroundType.Background; }
+		}
+
+		protected override void OnMouseDown(MouseEventArgs e)
+		{
+			if (DesignMode) return;
+			UpdateButtons(e);
+
+			if (e.Button == MouseButtons.Left && !_maximized && !Modal) //Modal check is a workaround until we figure out what's causing modals to resize when they should be dragged
+				ResizeForm(_resizeDir);
+			base.OnMouseDown(e);
+		}
+
+		protected override void OnMouseLeave(EventArgs e)
+		{
+			base.OnMouseLeave(e);
+			if (DesignMode) return;
+			_buttonState = ButtonState.None;
+			Invalidate();
+		}
+
+		protected override void OnMouseMove(MouseEventArgs e)
+		{
+			base.OnMouseMove(e);
+
+			if (DesignMode) return;
+
+			if (Sizable && ControlBox)
+			{
+				//True if the mouse is hovering over a child control
+				var isChildUnderMouse = GetChildAtPoint(e.Location) != null;
+				//isChildUnderMouse = false;
+
+				if (e.Location.X < BORDER_WIDTH && e.Location.Y > Height - BORDER_WIDTH && !isChildUnderMouse && !_maximized)
+				{
+					_resizeDir = ResizeDirection.BottomLeft;
+					Cursor = Cursors.SizeNESW;
+				}
+				else if (e.Location.X < BORDER_WIDTH && e.Location.Y < BORDER_WIDTH && !isChildUnderMouse && !_maximized)
+				{
+					_resizeDir = ResizeDirection.TopLeft;
+					Cursor = Cursors.SizeNWSE;
+				}
+				else if (e.Location.X > Width - BORDER_WIDTH && e.Location.Y < BORDER_WIDTH && !isChildUnderMouse && !_maximized && !_xButtonBounds.Contains(e.Location))
+				{
+					_resizeDir = ResizeDirection.TopRight;
+					Cursor = Cursors.SizeNESW;
+				}
+				else if (e.Location.X < BORDER_WIDTH && !isChildUnderMouse && !_maximized)
+				{
+					_resizeDir = ResizeDirection.Left;
+					Cursor = Cursors.SizeWE;
+				}
+				else if (e.Location.X > Width - BORDER_WIDTH && e.Location.Y > Height - BORDER_WIDTH && !isChildUnderMouse && !_maximized)
+				{
+					_resizeDir = ResizeDirection.BottomRight;
+					Cursor = Cursors.SizeNWSE;
+				}
+				else if (e.Location.X > Width - BORDER_WIDTH && !isChildUnderMouse && !_maximized)
+				{
+					_resizeDir = ResizeDirection.Right;
+					Cursor = Cursors.SizeWE;
+				}
+				else if (e.Location.Y > Height - BORDER_WIDTH && !isChildUnderMouse && !_maximized)
+				{
+					_resizeDir = ResizeDirection.Bottom;
+					Cursor = Cursors.SizeNS;
+				}
+				else if (e.Location.Y < BORDER_WIDTH && e.Location.X < _minButtonBounds.X && !isChildUnderMouse && !_maximized)
+				{
+					_resizeDir = ResizeDirection.Top;
+					Cursor = Cursors.SizeNS;
+				}
+				else
+				{
+					_resizeDir = ResizeDirection.None;
+
+					//Only reset the cursor when needed, this prevents it from flickering when a child control changes the cursor to its own needs
+					if (_resizeCursors.Contains(Cursor))
+					{
+						Cursor = Cursors.Default;
+					}
+				}
+			}
+
+			UpdateButtons(e);
+		}
+
+		protected void OnGlobalMouseMove(object sender, MouseEventArgs e)
+		{
+			if (IsDisposed || System.Threading.Thread.CurrentThread.ManagedThreadId != _threadId) return;
+			// Convert to client position and pass to Form.MouseMove
+			var clientCursorPos = PointToClient(e.Location);
+			var newE = new MouseEventArgs(MouseButtons.None, 0, clientCursorPos.X, clientCursorPos.Y, 0);
+			OnMouseMove(newE);
+		}
+
+		private void UpdateButtons(MouseEventArgs e, bool up = false)
+		{
+			if (DesignMode) return;
+			var oldState = _buttonState;
+			bool showMin = MinimizeBox && ControlBox;
+			bool showMax = MaximizeBox && ControlBox;
+
+			if (e.Button == MouseButtons.Left && !up)
+			{
+				if (showMin && !showMax && _maxButtonBounds.Contains(e.Location))
+					_buttonState = ButtonState.MinDown;
+				else if (showMin && showMax && _minButtonBounds.Contains(e.Location))
+					_buttonState = ButtonState.MinDown;
+				else if (showMax && _maxButtonBounds.Contains(e.Location))
+					_buttonState = ButtonState.MaxDown;
+				else if (ControlBox && _xButtonBounds.Contains(e.Location))
+					_buttonState = ButtonState.XDown;
+				else
+					_buttonState = ButtonState.None;
+			}
+			else
+			{
+				if (showMin && !showMax && _maxButtonBounds.Contains(e.Location))
+				{
+					_buttonState = ButtonState.MinOver;
+
+					if (oldState == ButtonState.MinDown && up)
+						WindowState = FormWindowState.Minimized;
+				}
+				else if (showMin && showMax && _minButtonBounds.Contains(e.Location))
+				{
+					_buttonState = ButtonState.MinOver;
+
+					if (oldState == ButtonState.MinDown && up)
+						WindowState = FormWindowState.Minimized;
+				}
+				else if (MaximizeBox && ControlBox && _maxButtonBounds.Contains(e.Location))
+				{
+					_buttonState = ButtonState.MaxOver;
+
+					if (oldState == ButtonState.MaxDown && up)
+						MaximizeWindow(!_maximized);
+
+				}
+				else if (ControlBox && _xButtonBounds.Contains(e.Location))
+				{
+					_buttonState = ButtonState.XOver;
+
+					if (oldState == ButtonState.XDown && up)
+						Close();
+				}
+				else _buttonState = ButtonState.None;
+			}
+
+			if (oldState != _buttonState) Invalidate();
+		}
+
+		private void MaximizeWindow(bool maximize)
+		{
+			if (!MaximizeBox || !ControlBox) return;
+
+			_maximized = maximize;
+
+			if (maximize)
+			{
+				IntPtr monitorHandle = MonitorFromWindow(Handle, MONITOR_DEFAULTTONEAREST);
+				MONITORINFOEX monitorInfo = new MONITORINFOEX();
+				GetMonitorInfo(new HandleRef(null, monitorHandle), monitorInfo);
+				_previousSize = Size;
+				_previousLocation = Location;
+				Size = new Size(monitorInfo.rcWork.Width(), monitorInfo.rcWork.Height());
+				Location = new Point(monitorInfo.rcWork.left, monitorInfo.rcWork.top);
+			}
+			else
+			{
+				Size = _previousSize;
+				Location = _previousLocation;
+			}
+
+		}
+
+		protected override void OnMouseUp(MouseEventArgs e)
+		{
+			if (DesignMode) return;
+			UpdateButtons(e, true);
+
+			base.OnMouseUp(e);
+			ReleaseCapture();
+		}
+
+		private void ResizeForm(ResizeDirection direction)
+		{
+			if (DesignMode) return;
+			var dir = -1;
+			switch (direction)
+			{
+				case ResizeDirection.BottomLeft:
+					dir = HTBOTTOMLEFT;
+					break;
+				case ResizeDirection.Left:
+					dir = HTLEFT;
+					break;
+				case ResizeDirection.Right:
+					dir = HTRIGHT;
+					break;
+				case ResizeDirection.BottomRight:
+					dir = HTBOTTOMRIGHT;
+					break;
+				case ResizeDirection.Bottom:
+					dir = HTBOTTOM;
+					break;
+				case ResizeDirection.Top:
+					dir = HTTOP;
+					break;
+				case ResizeDirection.TopLeft:
+					dir = HTTOPLEFT;
+					break;
+				case ResizeDirection.TopRight:
+					dir = HTTOPRIGHT;
+					break;
+			}
+
+			ReleaseCapture();
+			if (dir != -1)
+			{
+				SendMessage(Handle, WM_NCLBUTTONDOWN, (IntPtr)dir, (IntPtr)0);
+			}
+		}
+
+		private const int FormPadding = 14;
+
+		protected override void OnResize(EventArgs e)
+		{
+			base.OnResize(e);
+
+			_minButtonBounds = new Rectangle((Width - FormPadding / 2) - 3 * STATUS_BAR_BUTTON_WIDTH, 0, STATUS_BAR_BUTTON_WIDTH, StatusBarHeight);
+			_maxButtonBounds = new Rectangle((Width - FormPadding / 2) - 2 * STATUS_BAR_BUTTON_WIDTH, 0, STATUS_BAR_BUTTON_WIDTH, StatusBarHeight);
+			_xButtonBounds = new Rectangle((Width - FormPadding / 2) - STATUS_BAR_BUTTON_WIDTH, 0, STATUS_BAR_BUTTON_WIDTH, StatusBarHeight);
+			_statusBarBounds = new Rectangle(1, 1, Width - 2, StatusBarHeight - 1);
+			_actionBarBounds = new Rectangle(0, StatusBarHeight, Width, ACTION_BAR_HEIGHT);
+			Invalidate(true);
+		}
+
+		protected override void OnPaint(PaintEventArgs e)
+		{
+			var g = e.Graphics;
+
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
+			g.Clear(skin.Background.Normal);
+			g.FillRectangle(skin.PrimaryDarkColor.GetBrush(VisualState.Normal), _statusBarBounds);
+
+			//Draw border
+			Pen borderPen = skin.PrimaryLightColor.GetPen(VisualState.Normal, false, Enabled);
+			g.DrawRectangle(borderPen, 0, 0, Width - 1, Height - 1);
+
+			// Determine whether or not we even should be drawing the buttons.
+			bool showMin = MinimizeBox && ControlBox;
+			bool showMax = MaximizeBox && ControlBox;
+			SolidBrush hoverBrush = skin.PrimaryDarkColor.GetBrush(VisualState.Hover);
+			SolidBrush downBrush = skin.PrimaryDarkColor.GetBrush(VisualState.Pressed);
+
+			// When MaximizeButton == false, the minimize button will be painted in its place
+			if (_buttonState == ButtonState.MinOver && showMin)
+			{
+				g.FillRectangle(hoverBrush, showMax ? _minButtonBounds : _maxButtonBounds);
+			}
+
+			if (_buttonState == ButtonState.MinDown && showMin)
+			{
+				g.FillRectangle(downBrush, showMax ? _minButtonBounds : _maxButtonBounds);
+			}
+
+			if (_buttonState == ButtonState.MaxOver && showMax)
+			{
+				g.FillRectangle(hoverBrush, _maxButtonBounds);
+			}
+
+			if (_buttonState == ButtonState.MaxDown && showMax)
+			{
+				g.FillRectangle(downBrush, _maxButtonBounds);
+			}
+
+			if (ControlBox && (_buttonState == ButtonState.XOver || _buttonState == ButtonState.XDown))
+			{
+				using (SolidBrush br = new SolidBrush(skin.ErrorBackColor))
+				{
+					if (_buttonState == ButtonState.XOver)
+					{
+						g.FillRectangle(br, _xButtonBounds);
+					}
+					else if (_buttonState == ButtonState.XDown)
+					{
+						g.FillRectangle(br, _xButtonBounds);
+					}
+				}
+			}
+
+			using (var formButtonsPen = new Pen(skin.PrimaryDarkColor.ForeColor, 2))
+			{
+				// Minimize button.
+				if (showMin)
+				{
+					int x = showMax ? _minButtonBounds.X : _maxButtonBounds.X;
+					int y = showMax ? _minButtonBounds.Y : _maxButtonBounds.Y;
+
+					g.DrawLine(
+						formButtonsPen,
+						x + (int)(_minButtonBounds.Width * 0.33),
+						y + (int)(_minButtonBounds.Height * 0.66),
+						x + (int)(_minButtonBounds.Width * 0.66),
+						y + (int)(_minButtonBounds.Height * 0.66)
+				   );
+				}
+
+				// Maximize button
+				if (showMax)
+				{
+					if (_maximized)
+					{
+						g.DrawRectangle(
+							formButtonsPen,
+							_maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.33) - 2,
+							_maxButtonBounds.Y + (int)(_maxButtonBounds.Height * 0.36) + 2,
+							(int)(_maxButtonBounds.Width * 0.39),
+							(int)(_maxButtonBounds.Height * 0.31)
+					   );
+						g.DrawRectangle(
+							formButtonsPen,
+							_maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.33) + 2,
+							_maxButtonBounds.Y + (int)(_maxButtonBounds.Height * 0.36) - 2,
+							(int)(_maxButtonBounds.Width * 0.39),
+							(int)(_maxButtonBounds.Height * 0.31)
+					   );
+					}
+					else
+					{
+						g.DrawRectangle(
+							formButtonsPen,
+							_maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.33),
+							_maxButtonBounds.Y + (int)(_maxButtonBounds.Height * 0.36),
+							(int)(_maxButtonBounds.Width * 0.39),
+							(int)(_maxButtonBounds.Height * 0.31)
+					   );
+					}
+				}
+
+				// Close button
+				if (ControlBox)
+				{
+					g.DrawLine(
+						formButtonsPen,
+						_xButtonBounds.X + (int)(_xButtonBounds.Width * 0.33),
+						_xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.33),
+						_xButtonBounds.X + (int)(_xButtonBounds.Width * 0.66),
+						_xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.66)
+				   );
+
+					g.DrawLine(
+						formButtonsPen,
+						_xButtonBounds.X + (int)(_xButtonBounds.Width * 0.66),
+						_xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.33),
+						_xButtonBounds.X + (int)(_xButtonBounds.Width * 0.33),
+						_xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.66));
+				}
+			}
+
+			//Form title
+			bool showIcon = ShowIcon && Icon != null && ControlBox;
+			if (showIcon)
+			{
+				g.DrawIcon(Icon, new Rectangle(6, StatusBarHeight / 2 - IconSize / 2, IconSize, IconSize));
+			}
+			if (ShowTitleBar)
+			{
+				using (SolidBrush foreBrush = new SolidBrush(skin.PrimaryDarkColor.ForeColor))
+				{
+					g.DrawString(Text, Skin.HeaderFont, foreBrush, new Rectangle(FormPadding + (showIcon ? IconSize : 0), 0, Width, StatusBarHeight), new StringFormat { LineAlignment = StringAlignment.Center });
+				}
+			}
+		}
+		#endregion
+
+		public SkinnedForm()
+		{
+			_threadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
+			FormBorderStyle = FormBorderStyle.None;
+			Sizable = true;
+			DoubleBuffered = true;
+			SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
+
+			// This enables the form to trigger the MouseMove event even when mouse is over another control
+			Application.AddMessageFilter(new MouseMessageFilter());
+			MouseMessageFilter.MouseMove += OnGlobalMouseMove;
+		}
+
+		protected override void OnLoad(EventArgs e)
+		{
+			base.OnLoad(e);
+			if (DesignMode) { return; }
+			_mailbox = Shell.Instance.PostOffice.GetMailbox();
+			_mailbox.Subscribe<Skin>(CoreDesktopMessages.SkinChanged, OnSkinChanged);
+
+			OnSkinChanged(SkinManager.Instance.CurrentSkin);
+		}
+
+		private void OnSkinChanged(Skin skin)
+		{
+			Invalidate(true);
+			BackColor = skin.Background.Normal;
+			ForeColor = skin.Surface.ForeColor;
+
+			foreach (Control ctl in Controls)
+			{
+				SkinManager.Instance.ReskinControl(ctl, skin);
+			}
+			OnUpdateSkin(skin);
+		}
+
+		protected virtual void OnUpdateSkin(Skin skin)
+		{
+		}
+	}
+
+	public class MouseMessageFilter : IMessageFilter
+	{
+		private const int WM_MOUSEMOVE = 0x0200;
+
+		public static event MouseEventHandler MouseMove;
+
+		public bool PreFilterMessage(ref Message m)
+		{
+			if (m.Msg == WM_MOUSEMOVE)
+			{
+				if (MouseMove != null)
+				{
+					int x = Control.MousePosition.X, y = Control.MousePosition.Y;
+
+					MouseMove(null, new MouseEventArgs(MouseButtons.None, 0, x, y, 0));
+				}
+			}
+			return false;
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedGroupBox.cs b/editor source/Desktop/Skinning/SkinnedGroupBox.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f991a8d1f64f4435ebdac7ffa2ede300893f7397
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedGroupBox.cs	
@@ -0,0 +1,49 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedGroupBox : GroupBox, ISkinControl, ISkinnedPanel
+	{
+		public SkinnedBackgroundType PanelType
+		{
+			get { return SkinnedBackgroundType.Surface; }
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			BackColor = skin.Surface.Normal;
+		}
+
+		protected override void OnPaint(PaintEventArgs e)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
+			Graphics g = e.Graphics;
+			g.Clear(Enabled ? BackColor : skin.Surface.Disabled);
+
+			Rectangle rect = new Rectangle(ClientRectangle.Left + 1, ClientRectangle.Top, ClientRectangle.Width - 3, ClientRectangle.Height - 2);
+			Pen pen = skin.Surface.GetBorderPen(VisualState.Normal, false, Enabled);
+			g.DrawRectangle(pen, rect);
+			using (Pen shadow = new Pen(skin.SurfaceShadowColor))
+			{
+				g.DrawLine(shadow, rect.Left - 1, rect.Y, rect.Left - 1, rect.Bottom + 1);
+				g.DrawLine(shadow, rect.Right + 1, rect.Y, rect.Right + 1, rect.Bottom + 1);
+				g.DrawLine(shadow, ClientRectangle.X, rect.Bottom + 1, rect.Right, rect.Bottom + 1);
+			}
+
+			string text = Text;
+			Font font = Skin.HeaderFont;
+			SizeF textSize = g.MeasureString(text, font);
+			RectangleF textRect = new RectangleF(ClientRectangle.Left + 4, ClientRectangle.Top + 1, textSize.Width, textSize.Height);
+			using (Brush textBrush = new SolidBrush(Enabled ? skin.PrimaryForeColor : skin.Surface.DisabledForeColor))
+			{
+				using (Brush back = new SolidBrush(BackColor))
+				{
+					g.FillRectangle(back, textRect);
+				}
+				g.DrawString(text, font, textBrush, textRect);
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedIcon.cs b/editor source/Desktop/Skinning/SkinnedIcon.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9c337a02aed2459086325f002de4395700a28785
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedIcon.cs	
@@ -0,0 +1,24 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedIcon : SkinnedButton
+	{
+		protected override void OnPaint(PaintEventArgs pevent)
+		{
+			Color backColor = DesignMode ? SystemColors.Control : this.GetSkinnedPanelBackColor();
+			if (MouseState == VisualState.Hover)
+			{
+				backColor = SkinManager.Instance.CurrentSkin.PrimaryColor.Hover;
+			}
+			pevent.Graphics.Clear(backColor);
+
+			if (Image != null)
+			{
+				pevent.Graphics.DrawImage(Image, ClientRectangle.X + ClientRectangle.Width / 2 - Image.Width / 2, ClientRectangle.Y + ClientRectangle.Height / 2 - Image.Height / 2);
+			}
+		}
+
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedLabel.cs b/editor source/Desktop/Skinning/SkinnedLabel.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1bf3700d5174921c4d52a73439ec0ad93e7feab5
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedLabel.cs	
@@ -0,0 +1,136 @@
+using System;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedLabel : Label, ISkinControl
+	{
+		private SkinnedLabelLevel _level = SkinnedLabelLevel.Normal;
+		public SkinnedLabelLevel Level
+		{
+			get { return _level; }
+			set
+			{
+				_level = value;
+				OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+				Invalidate(true);
+			}
+		}
+
+		private SkinnedHighlight _highlight = SkinnedHighlight.Normal;
+		public SkinnedHighlight Highlight
+		{
+			get { return _highlight; }
+			set
+			{
+				_highlight = value;
+				OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+				Invalidate(true);
+			}
+		}
+
+		protected override void OnHandleDestroyed(EventArgs e)
+		{
+			SkinManager.Instance.UnregisterControl(this);
+			base.OnHandleDestroyed(e);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			switch (Level)
+			{
+				case SkinnedLabelLevel.Normal:
+					ForeColor = DesignMode ? ForeColor : this.GetSkinnedPanelForeColor();
+					Font = Skin.TextFont;
+					break;
+				case SkinnedLabelLevel.Label:
+					ForeColor = skin.LabelForeColor;
+					Font = Skin.TextFont;
+					break;
+				case SkinnedLabelLevel.Heading:
+					ForeColor = skin.PrimaryForeColor;
+					Font = Skin.HeaderFont;
+					break;
+				case SkinnedLabelLevel.Primary:
+					ForeColor = skin.PrimaryColor.ForeColor;
+					Font = Skin.TextFont;
+					break;
+				case SkinnedLabelLevel.PrimaryLight:
+					ForeColor = skin.PrimaryLightColor.ForeColor;
+					Font = Skin.TextFont;
+					break;
+				case SkinnedLabelLevel.PrimaryDark:
+					ForeColor = skin.PrimaryDarkColor.ForeColor;
+					Font = Skin.TextFont;
+					break;
+				case SkinnedLabelLevel.Secondary:
+					ForeColor = skin.SecondaryColor.ForeColor;
+					Font = Skin.TextFont;
+					break;
+				case SkinnedLabelLevel.SecondaryLight:
+					ForeColor = skin.SecondaryLightColor.ForeColor;
+					Font = Skin.TextFont;
+					break;
+				case SkinnedLabelLevel.SecondaryDark:
+					ForeColor = skin.SecondaryDarkColor.ForeColor;
+					Font = Skin.TextFont;
+					break;
+				case SkinnedLabelLevel.Title:
+					ForeColor = skin.PrimaryColor.ForeColor;
+					Font = Skin.TitleFont;
+					break;
+				case SkinnedLabelLevel.Finished:
+					ForeColor = skin.GoodForeColor;
+					Font = Skin.CompletionFont;
+					break;
+			}
+
+			switch (Highlight)
+			{
+				case SkinnedHighlight.Label:
+					ForeColor = skin.LabelForeColor;
+					break;
+				case SkinnedHighlight.Good:
+					ForeColor = skin.GoodForeColor;
+					break;
+				case SkinnedHighlight.Bad:
+					ForeColor = skin.BadForeColor;
+					break;
+				case SkinnedHighlight.Heading:
+					ForeColor = skin.PrimaryForeColor;
+					break;
+			}
+		}
+	}
+
+	public enum SkinnedLabelLevel
+	{
+		Normal,
+		Label,
+		Heading,
+		Primary,
+		PrimaryLight,
+		PrimaryDark,
+		Secondary,
+		SecondaryLight,
+		SecondaryDark,
+		Title,
+		Finished,
+	}
+
+	public class SkinnedLinkLabel : LinkLabel, ISkinControl
+	{
+		protected override void OnCreateControl()
+		{
+			base.OnCreateControl();
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			Font = Skin.TextFont;
+			ForeColor = skin.Surface.ForeColor;
+			LinkColor = skin.Blue;
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedListBox.cs b/editor source/Desktop/Skinning/SkinnedListBox.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6266fb3fbe51527730150b9079e7eaf4fdd834ee
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedListBox.cs	
@@ -0,0 +1,91 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedListBox : ListBox, ISkinControl
+	{
+		protected override void OnCreateControl()
+		{
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			ForeColor = skin.Surface.ForeColor;
+			BackColor = skin.FieldBackColor;
+			Font = Skin.TextFont;
+		}
+
+		public void RefreshListItems()
+		{
+			this.RefreshItems();
+		}
+	}
+
+	public class SkinnedCheckedListBox : CheckedListBox, ISkinControl
+	{
+		public SkinnedCheckedListBox()
+		{
+			DoubleBuffered = true;
+		}
+
+		protected override void OnCreateControl()
+		{
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			ForeColor = skin.Surface.ForeColor;
+			BackColor = skin.FieldBackColor;
+			Font = Skin.TextFont;
+		}
+
+		protected override void OnDrawItem(DrawItemEventArgs e)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
+			e.DrawBackground();
+			if (e.Index >= Items.Count)
+			{
+				return;
+			}
+			bool isChecked = GetItemChecked(e.Index);
+
+			Graphics g = e.Graphics;
+
+			using (Brush boxColor = new SolidBrush(skin.FieldBackColor))
+			{
+				int start = e.Bounds.Y + e.Bounds.Height / 2 - SkinnedCheckBox.CheckBoxSize / 2;
+				Rectangle rect = new Rectangle(e.Bounds.X + 1, start, SkinnedCheckBox.CheckBoxSize, SkinnedCheckBox.CheckBoxSize);
+				g.FillRectangle(boxColor, rect);
+				Pen borderPen = skin.PrimaryColor.GetPen(VisualState.Normal, Focused, Enabled);
+				g.DrawRectangle(borderPen, rect);
+
+				if (isChecked)
+				{
+					Brush checkBrush = skin.PrimaryColor.GetBrush(VisualState.Normal, true, Enabled);
+					g.FillRectangle(checkBrush, rect);
+					using (Pen check = new Pen(skin.PrimaryColor.ForeColor, 2))
+					{
+						for (int i = 0; i < SkinnedCheckBox.CheckmarkPoints.Length - 1; i++)
+						{
+							Point pt0 = new Point(SkinnedCheckBox.CheckmarkPoints[i].X + e.Bounds.X + 1, SkinnedCheckBox.CheckmarkPoints[i].Y + e.Bounds.Y - 1);
+							Point pt1 = new Point(SkinnedCheckBox.CheckmarkPoints[i + 1].X + e.Bounds.X + 1, SkinnedCheckBox.CheckmarkPoints[i + 1].Y + e.Bounds.Y - 1);
+							g.DrawLine(check, pt0, pt1);
+						}
+					}
+				}
+			}
+
+			using (StringFormat sf = new StringFormat { LineAlignment = StringAlignment.Center, Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.NoWrap })
+			{
+				using (Brush brush = new SolidBrush(ForeColor))
+				{
+					e.Graphics.DrawString(Items[e.Index].ToString(), Font, brush, new Rectangle(e.Bounds.Left + SkinnedCheckBox.CheckBoxSize + 3, e.Bounds.Top, e.Bounds.Width - SkinnedCheckBox.CheckBoxSize - 3, e.Bounds.Height), sf);
+				}
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedListView.cs b/editor source/Desktop/Skinning/SkinnedListView.cs
new file mode 100644
index 0000000000000000000000000000000000000000..40ac16b6e4eb5ec155f131cea9f5051dc3683287
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedListView.cs	
@@ -0,0 +1,101 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedListView : ListView, ISkinControl
+	{
+		private StringFormat _sf = new StringFormat() { LineAlignment = StringAlignment.Center, FormatFlags = StringFormatFlags.NoWrap, Trimming = StringTrimming.EllipsisCharacter };
+		private SolidBrush _fieldBrush = new SolidBrush(Color.White);
+		private SolidBrush _foreBrush = new SolidBrush(Color.Black);
+
+		public SkinnedListView()
+		{
+			OwnerDraw = true;
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			_fieldBrush.Color = skin.FieldBackColor;
+			_foreBrush.Color = skin.Surface.ForeColor;
+			ForeColor = skin.Surface.ForeColor;
+			BackColor = skin.FieldBackColor;
+			Font = Skin.TextFont;
+			Invalidate(true);
+		}
+
+		protected override void OnPaintBackground(PaintEventArgs pevent)
+		{
+			base.OnPaintBackground(pevent);
+		}
+
+		protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			Graphics g = e.Graphics;
+			base.OnDrawColumnHeader(e);
+			SolidBrush back = skin.Surface.GetBrush(VisualState.Normal, false, true);
+			g.FillRectangle(back, e.Bounds);
+			using (SolidBrush fore = new SolidBrush(skin.Surface.ForeColor))
+			{
+				Rectangle textRect = new Rectangle(e.Bounds.X + 5, e.Bounds.Y, e.Bounds.Width - 5, e.Bounds.Height);
+				g.DrawString(e.Header.Text, Font, fore, textRect, _sf);
+			}
+			Pen border = skin.Surface.GetBorderPen(VisualState.Normal, false, true);
+			g.DrawLine(border, e.Bounds.X, e.Bounds.Bottom - 1, e.Bounds.Right, e.Bounds.Bottom - 1);
+			if (e.ColumnIndex > 0)
+			{
+				g.DrawLine(border, e.Bounds.X, e.Bounds.Y, e.Bounds.X, e.Bounds.Bottom - 2);
+			}
+		}
+
+		protected override void OnDrawItem(DrawListViewItemEventArgs e)
+		{
+			base.OnDrawItem(e);
+			if (Columns.Count <= 0)
+			{
+				Rectangle bounds = e.Bounds;
+				if (CheckBoxes)
+				{
+					SkinnedCheckBox.RenderCheckbox(e.Graphics, e.Bounds.X, e.Bounds.Y, SkinnedFieldType.Primary, e.Item.Checked ? CheckState.Checked : CheckState.Unchecked, VisualState.Normal, Enabled);
+
+					bounds.X += SkinnedCheckBox.CheckBoxSize + 4;
+					bounds.Width -= SkinnedCheckBox.CheckBoxSize + 4;
+				}
+				DrawListItem(e.Graphics, e.Item, bounds, SkinManager.Instance.CurrentSkin, e.Item.Text);
+			}
+		}
+
+		protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
+			base.OnDrawSubItem(e);
+			string text = e.SubItem.Text;
+			DrawListItem(e.Graphics, e.Item, e.Bounds, skin, e.SubItem.Text);
+		}
+
+		private void DrawListItem(Graphics g, ListViewItem item, Rectangle bounds, Skin skin, string text)
+		{
+			SolidBrush fore = _foreBrush;
+			Color oldColor = fore.Color;
+			if (item.Selected)
+			{
+				SolidBrush brush = skin.PrimaryLightColor.GetBrush(VisualState.Normal, false, Enabled);
+				g.FillRectangle(brush, bounds);
+				fore.Color = Enabled ? skin.PrimaryLightColor.ForeColor : skin.PrimaryLightColor.DisabledForeColor;
+			}
+			else
+			{
+				using (SolidBrush back = new SolidBrush(BackColor))
+				{
+					g.FillRectangle(back, bounds);
+				}
+			}
+
+			g.DrawString(text, Skin.TextFont, fore, bounds, _sf);
+
+			fore.Color = oldColor;
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedMenuStrip.cs b/editor source/Desktop/Skinning/SkinnedMenuStrip.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4838887406f736f2186db8d9fbd9e0e0831e4d2d
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedMenuStrip.cs	
@@ -0,0 +1,25 @@
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedMenuStrip : MenuStrip, ISkinControl
+	{
+		private SkinnedBackgroundType _background = SkinnedBackgroundType.PrimaryDark;
+		public SkinnedBackgroundType Background
+		{
+			get { return _background; }
+			set
+			{
+				_background = value;
+				Tag = _background.ToString();
+				OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+				Invalidate(true);
+			}
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			Renderer = new SkinnedToolStripRenderer(skin, this);
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedNumericUpDown.cs b/editor source/Desktop/Skinning/SkinnedNumericUpDown.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e07e59d5cf1fd902c3c33ecdcc1009025584fb53
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedNumericUpDown.cs	
@@ -0,0 +1,19 @@
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedNumericUpDown : NumericUpDown, ISkinControl
+	{
+		protected override void OnCreateControl()
+		{
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			Font = Skin.TextFont;
+			ForeColor = skin.Surface.ForeColor;
+			BackColor = skin.FieldBackColor;
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedPanel.cs b/editor source/Desktop/Skinning/SkinnedPanel.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a853cf4086e16d02b7bac5510829e73e79367fa1
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedPanel.cs	
@@ -0,0 +1,114 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedPanel : Panel, ISkinControl, ISkinnedPanel
+	{
+		private SkinnedBackgroundType _type = SkinnedBackgroundType.Background;
+		public SkinnedBackgroundType PanelType
+		{
+			get { return _type; }
+			set
+			{
+				_type = value;
+				Invalidate(true);
+			}
+		}
+
+		private TabSide _tabSide = TabSide.None;
+		public TabSide TabSide
+		{
+			get { return _tabSide; }
+			set { _tabSide = value; Invalidate(); }
+		}
+
+		public SkinnedPanel()
+		{
+			DoubleBuffered = true;
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			BackColor = skin.GetBackColor(PanelType);
+		}
+
+		protected override void OnPaint(PaintEventArgs e)
+		{
+			if (_tabSide != TabSide.None)
+			{
+				ColorSet set = SkinManager.Instance.CurrentSkin.GetColorSet(PanelType);
+				Pen borderPen = set.GetBorderPen(VisualState.Normal, false, Enabled);
+				e.Graphics.DrawRectangle(borderPen, 0, 0, Width - 1, Height - 1);
+			}
+
+			base.OnPaint(e);
+		}
+	}
+
+	public enum TabSide
+	{
+		None,
+		Top,
+		Left
+	}
+
+	public interface ISkinnedPanel
+	{
+		SkinnedBackgroundType PanelType { get; }
+	}
+
+	public static class SkinnedExtensions
+	{
+		/// <summary>
+		/// Gets the back color of the nearest ISkinnedPanel ancestor
+		/// </summary>
+		/// <param name="ctl"></param>
+		/// <returns></returns>
+		public static Color GetSkinnedPanelBackColor(this ISkinControl skinCtl)
+		{
+			SkinManager.Instance.RegisterControl(skinCtl);
+			Control ctl = skinCtl as Control;
+			Control parent = ctl.Parent;
+			while (parent != null)
+			{
+				ISkinnedPanel skinnedPanel = parent as ISkinnedPanel;
+				if (skinnedPanel != null && skinnedPanel.PanelType != SkinnedBackgroundType.Transparent)
+				{
+					return SkinManager.Instance.CurrentSkin.GetBackColor(skinnedPanel.PanelType);
+				}
+				else if (parent is TabPage)
+				{
+					//TabPages get set by an associated SkinnedTabStrip, which isn't a direct ancestor
+					return parent.BackColor;
+				}
+				parent = parent.Parent;
+			}
+
+			return ctl.BackColor;
+		}
+
+		public static Color GetSkinnedPanelForeColor(this ISkinControl skinCtl)
+		{
+			SkinManager.Instance.RegisterControl(skinCtl);
+			Control ctl = skinCtl as Control;
+			Control parent = ctl.Parent;
+			while (parent != null)
+			{
+				ISkinnedPanel skinnedPanel = parent as ISkinnedPanel;
+				if (skinnedPanel != null && skinnedPanel.PanelType != SkinnedBackgroundType.Transparent)
+				{
+					return SkinManager.Instance.CurrentSkin.GetForeColor(skinnedPanel.PanelType);
+				}
+				else if (parent is TabPage)
+				{
+					//TabPages get set by an associated SkinnedTabStrip, which isn't a direct ancestor
+					return parent.ForeColor;
+				}
+				parent = parent.Parent;
+			}
+
+			return ctl.ForeColor;
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedProgressBar.cs b/editor source/Desktop/Skinning/SkinnedProgressBar.cs
new file mode 100644
index 0000000000000000000000000000000000000000..dbcaade3873547d052c2d2d17e47133dbac4c72f
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedProgressBar.cs	
@@ -0,0 +1,24 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedProgressBar : ProgressBar
+	{
+		public SkinnedProgressBar()
+		{
+			this.SetStyle(ControlStyles.UserPaint, true);
+		}
+
+		protected override void OnPaint(PaintEventArgs e)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			Rectangle rec = e.ClipRectangle;
+
+			rec.Width = (int)(rec.Width * ((double)Value / Maximum)) - 4;
+			e.Graphics.FillRectangle(skin.PrimaryLightColor.GetBrush(VisualState.Normal), e.ClipRectangle);
+			rec.Height = rec.Height - 4;
+			e.Graphics.FillRectangle(skin.PrimaryColor.GetBrush(VisualState.Normal, false, Enabled), 2, 2, rec.Width, rec.Height);
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedRadioButton.cs b/editor source/Desktop/Skinning/SkinnedRadioButton.cs
new file mode 100644
index 0000000000000000000000000000000000000000..860d5fa31b07141557d14dac3b411df373399797
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedRadioButton.cs	
@@ -0,0 +1,169 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedRadioButton : RadioButton, ISkinControl
+	{
+		public VisualState MouseState { get; private set; }
+		private static StringFormat _sf = new StringFormat() { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Near, FormatFlags = StringFormatFlags.NoWrap };
+
+		private SkinnedFieldType _fieldType = SkinnedFieldType.Primary;
+		public SkinnedFieldType FieldType
+		{
+			get { return _fieldType; }
+			set { _fieldType = value; OnUpdateSkin(SkinManager.Instance.CurrentSkin); Invalidate(); }
+		}
+
+		protected override void OnCreateControl()
+		{
+			base.OnCreateControl();
+			if (DesignMode) { return; }
+
+			MouseEnter += MouseEnterEvent;
+			MouseLeave += MouseLeaveEvent;
+			MouseDown += MouseDownEvent;
+			MouseUp += MouseUpEvent;
+		}
+
+		private void _animator_OnUpdate(object sender, float e)
+		{
+			Invalidate();
+		}
+
+		private void MouseEnterEvent(object sender, EventArgs e)
+		{
+			MouseState = VisualState.Hover;
+			Invalidate();
+		}
+
+		private void MouseLeaveEvent(object sender, EventArgs e)
+		{
+			MouseState = VisualState.Normal;
+			Invalidate();
+		}
+
+		private void MouseDownEvent(object sender, MouseEventArgs e)
+		{
+			if (e.Button == MouseButtons.Left)
+			{
+				MouseState = VisualState.Pressed;
+				Invalidate();
+			}
+		}
+
+		private void MouseUpEvent(object sender, MouseEventArgs e)
+		{
+			MouseState = VisualState.Hover;
+			Invalidate();
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			Font = Skin.TextFont;
+		}
+
+		private const int RadioSize = 12;
+
+		protected override void OnPaint(PaintEventArgs pevent)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
+			Graphics g = pevent.Graphics;
+			g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
+
+			g.Clear(DesignMode ? Parent.BackColor : this.GetSkinnedPanelBackColor());
+
+			if (Appearance == Appearance.Normal)
+			{
+				ColorSet set = skin.GetWidgetColorSet(FieldType);
+				using (Brush boxColor = new SolidBrush(Enabled ? skin.FieldBackColor : skin.FieldDisabledBackColor))
+				{
+					int start = pevent.ClipRectangle.Y + pevent.ClipRectangle.Height / 2 - RadioSize / 2;
+					Rectangle rect = new Rectangle(0, start, RadioSize, RadioSize);
+					g.FillEllipse(boxColor, rect);
+					Pen borderPen = set.GetBorderPen(MouseState, Focused, Enabled);
+					g.DrawEllipse(borderPen, rect);
+
+					if (Checked)
+					{
+						Brush checkBrush = set.GetBrush(MouseState, Focused, Enabled);
+						g.FillEllipse(checkBrush, rect.X + 3, rect.Y + 3, rect.Width - 6, rect.Height - 6);
+					}
+				}
+
+				g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
+
+				Color foreColor = DesignMode ? ForeColor : this.GetSkinnedPanelForeColor();
+				int left = 4 + RadioSize;
+				Rectangle textRect = new Rectangle(left, 1, ClientRectangle.Width - left, ClientRectangle.Height - 2);
+				TextRenderer.DrawText(g, Text, Font, textRect, foreColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
+				if (Focused)
+				{
+					textRect = new Rectangle(left - 1, textRect.Y - 1, textRect.Width, textRect.Height + 3);
+					SkinManager.Instance.DrawFocusRectangle(g, textRect);
+				}
+			}
+			else
+			{
+				DrawButton(g, skin);
+			}
+		}
+
+		private void DrawButton(Graphics g, Skin skin)
+		{
+			ColorSet set = skin.GetFieldColorSet(FieldType, SkinnedLightLevel.Normal);
+
+			Rectangle bounds = ClientRectangle;
+
+			VisualState state = MouseState;
+			if (Checked)
+			{
+				//back
+				SolidBrush backColor = set.GetBrush(state, Focused, Enabled);
+				g.FillRectangle(backColor, bounds);
+
+				//text
+				SkinnedButton.DrawContent(g, bounds, set.ForeColor, Image, ImageAlign, Text, ContentAlignment.MiddleCenter);
+
+				if (Focused)
+				{
+					SkinManager.Instance.DrawFocusRectangle(g, bounds);
+				}
+
+				//border
+				Pen borderPen = set.GetBorderPen(state, true, Enabled);
+				g.DrawRectangle(borderPen, bounds.X, bounds.Y, bounds.Width - 1, bounds.Height - 1);
+			}
+			else
+			{
+				Color foreColor = skin.GetForeColor(FieldType);
+				Color backColor = Parent.BackColor;
+
+				//back
+				if (state == VisualState.Hover)
+				{
+					backColor = set.Hover;
+					foreColor = set.ForeColor;
+				}
+				else if (state == VisualState.Pressed)
+				{
+					backColor = set.Pressed;
+					foreColor = set.ForeColor;
+				}
+				using (SolidBrush br = new SolidBrush(backColor))
+				{
+					g.FillRectangle(br, bounds);
+				}
+				//text
+				SkinnedButton.DrawContent(g, bounds, foreColor, Image, ImageAlign, Text, ContentAlignment.MiddleCenter);
+
+				if (Focused)
+				{
+					SkinManager.Instance.DrawFocusRectangle(g, bounds);
+				}
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedRenderer.cs b/editor source/Desktop/Skinning/SkinnedRenderer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1c91d8f46acbd3fb76ee45ce1c1b4bcde6505b58
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedRenderer.cs	
@@ -0,0 +1,340 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedColorTable : ProfessionalColorTable
+	{
+		public Skin Skin;
+		public ColorSet MenuSet;
+		public ColorSet CheckedSet;
+		private SkinnedBackgroundType _type;
+
+		public SkinnedColorTable(Skin skin, Control owner)
+		{
+			Skin = skin;
+			ParseMenuType(owner, owner.Tag?.ToString());
+		}
+
+		private void ParseMenuType(Control owner, string tag)
+		{
+			if (string.IsNullOrEmpty(tag))
+			{
+				//use default for the control type
+				if (owner is StatusStrip)
+				{
+					_type = SkinnedBackgroundType.PrimaryDark;
+				}
+				else if (owner is MenuStrip)
+				{
+					_type = SkinnedBackgroundType.Primary;
+				}
+				else
+				{
+					_type = SkinnedBackgroundType.PrimaryLight;
+				}
+			}
+			else
+			{
+				Enum.TryParse(tag, out _type);
+			}
+
+			CheckedSet = Skin.PrimaryLightColor;
+			switch (_type)
+			{
+				case SkinnedBackgroundType.Surface:
+					MenuSet = Skin.Surface;
+					break;
+				case SkinnedBackgroundType.Primary:
+					MenuSet = Skin.PrimaryColor;
+					break;
+				case SkinnedBackgroundType.PrimaryDark:
+					MenuSet = Skin.PrimaryDarkColor;
+					break;
+				case SkinnedBackgroundType.PrimaryLight:
+					MenuSet = Skin.PrimaryLightColor;
+					CheckedSet = Skin.PrimaryDarkColor;
+					break;
+				case SkinnedBackgroundType.Secondary:
+					MenuSet = Skin.SecondaryColor;
+					CheckedSet = Skin.SecondaryLightColor;
+					break;
+				case SkinnedBackgroundType.SecondaryDark:
+					MenuSet = Skin.SecondaryDarkColor;
+					CheckedSet = Skin.SecondaryLightColor;
+					break;
+				case SkinnedBackgroundType.SecondaryLight:
+					MenuSet = Skin.SecondaryLightColor;
+					CheckedSet = Skin.SecondaryDarkColor;
+					break;
+				case SkinnedBackgroundType.Background:
+					MenuSet = Skin.Background;
+					CheckedSet = Skin.Surface;
+					break;
+				default:
+					MenuSet = Skin.PrimaryColor;
+					break;
+			}
+		}
+
+		//Toolstrip bottom border
+		public override Color ToolStripBorder
+		{
+			get { return Color.Transparent; }
+		}
+
+		// Status strip background
+		public override Color StatusStripGradientBegin
+		{
+			get { return MenuSet.Normal; }
+		}
+		public override Color StatusStripGradientEnd
+		{
+			get { return StatusStripGradientBegin; }
+		}
+
+		// Drop down menu border
+		public override Color MenuBorder
+		{
+			get { return MenuSet.Border; }
+		}
+
+		// All menu hover effect borders (not tool strip)
+		public override Color MenuItemBorder
+		{
+			get { return Color.Transparent; }
+		}
+		// Toolstrip selected+hover buttons
+		public override Color ButtonSelectedBorder
+		{
+			get { return MenuSet.BorderHover; }
+		}
+
+		// MenuStrip background
+		public override Color MenuStripGradientBegin
+		{
+			get { return MenuSet.Normal; }
+		}
+		public override Color MenuStripGradientEnd
+		{
+			get { return MenuStripGradientBegin; }
+		}
+
+		// top-level menu hover effect
+		public override Color MenuItemSelectedGradientBegin
+		{
+			get { return MenuSet.Hover; }
+		}
+		public override Color MenuItemSelectedGradientEnd
+		{
+			get { return MenuItemSelectedGradientBegin; }
+		}
+
+		// Toolstrip background
+		public override Color ToolStripGradientBegin
+		{
+			get { return MenuSet.Normal; }
+		}
+		public override Color ToolStripGradientMiddle
+		{
+			get { return ToolStripGradientBegin; }
+		}
+		public override Color ToolStripGradientEnd
+		{
+			get { return ToolStripGradientBegin; }
+		}
+
+		// Drop Down Menu background
+		public override Color ToolStripDropDownBackground
+		{
+			get { return Skin.Surface.Normal; }
+		}
+		public override Color ImageMarginGradientBegin
+		{
+			get { return ToolStripDropDownBackground; }
+		}
+		public override Color ImageMarginGradientMiddle
+		{
+			get { return ToolStripDropDownBackground; }
+		}
+		public override Color ImageMarginGradientEnd
+		{
+			get { return ToolStripDropDownBackground; }
+		}
+
+		// top-level menu button background with dropdown open
+		public override Color MenuItemPressedGradientBegin
+		{
+			get { return ToolStripDropDownBackground; }
+		}
+		public override Color MenuItemPressedGradientMiddle
+		{
+			get { return MenuItemPressedGradientBegin; }
+		}
+		public override Color MenuItemPressedGradientEnd
+		{
+			get { return MenuItemPressedGradientBegin; }
+		}
+
+		// Drop down Item hover effect
+		public override Color MenuItemSelected
+		{
+			get { return MenuSet.Hover; }
+		}
+
+		// Toolstrip item hover effect
+		public override Color ButtonSelectedGradientBegin
+		{
+			get { return MenuSet.Hover; }
+		}
+		public override Color ButtonSelectedGradientMiddle
+		{
+			get { return ButtonSelectedGradientBegin; }
+		}
+		public override Color ButtonSelectedGradientEnd
+		{
+			get { return ButtonSelectedGradientBegin; }
+		}
+
+		// Checked button background
+		public override Color ButtonCheckedGradientBegin
+		{
+			get { return CheckedSet.Normal; }
+		}
+		public override Color ButtonCheckedGradientMiddle
+		{
+			get { return ButtonCheckedGradientBegin; }
+		}
+		public override Color ButtonCheckedGradientEnd
+		{
+			get { return ButtonCheckedGradientBegin; }
+		}
+		public override Color CheckBackground
+		{
+			get { return ButtonCheckedGradientBegin; }
+		}
+		public override Color CheckSelectedBackground
+		{
+			get { return CheckedSet.Selected; }
+		}
+		public override Color CheckPressedBackground
+		{
+			get { return CheckSelectedBackground; }
+		}
+
+		// Separator
+		public override Color SeparatorDark
+		{
+			get { return Skin.Separator; }
+		}
+		public override Color SeparatorLight
+		{
+			get { return SeparatorDark; }
+		}
+
+		// Overflow button background
+		public override Color OverflowButtonGradientBegin
+		{
+			get { return MenuSet.Normal; }
+		}
+		public override Color OverflowButtonGradientMiddle
+		{
+			get { return OverflowButtonGradientBegin; }
+		}
+		public override Color OverflowButtonGradientEnd
+		{
+			get { return OverflowButtonGradientBegin; }
+		}
+		public override Color ButtonPressedGradientBegin
+		{
+			get { return MenuSet.Pressed; }
+		}
+		public override Color ButtonPressedGradientMiddle
+		{
+			get { return ButtonPressedGradientBegin; }
+		}
+		public override Color ButtonPressedGradientEnd
+		{
+			get { return ButtonPressedGradientBegin; }
+		}
+	}
+
+	public class SkinnedToolStripRenderer : ToolStripProfessionalRenderer
+	{
+		public ColorSet MenuSet;
+
+		public SkinnedToolStripRenderer(Skin skin, Control owner) : base(new SkinnedColorTable(skin, owner))
+		{
+			Skin = skin;
+			RoundedEdges = false;
+			MenuSet = (ColorTable as SkinnedColorTable).MenuSet;
+		}
+
+		public Skin Skin;
+
+		private Color GetForeColor(ToolStripItem item)
+		{
+			if (item.IsOnDropDown)
+			{
+				if (item.IsOnOverflow)
+				{
+					return item.Selected ? MenuSet.ForeColor : Skin.Surface.ForeColor;
+				}
+				else
+				{
+					return item.Selected ? MenuSet.ForeColor : Skin.Surface.ForeColor;
+				}
+			}
+			else
+			{
+				ToolStripMenuItem mi = item as ToolStripMenuItem;
+				if (mi != null)
+				{
+					if (mi.DropDownItems.Count > 0 && item.Pressed)
+					{
+						return Skin.Surface.ForeColor;
+					}
+					else
+					{
+						return MenuSet.ForeColor;
+					}
+				}
+				else
+				{
+					ToolStripButton btn = item as ToolStripButton;
+					ToolStripDropDownItem dropdownBtn = item as ToolStripDropDownItem;
+					if (btn != null && btn.Checked && !item.Pressed && !item.Selected)
+					{
+						return (ColorTable as SkinnedColorTable).CheckedSet.ForeColor;
+					}
+					else if (dropdownBtn != null && dropdownBtn.Pressed)
+					{
+						return Skin.Surface.ForeColor;
+					}
+					return MenuSet.ForeColor;
+				}
+			}
+		}
+
+		protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
+		{
+			e.TextColor = GetForeColor(e.Item);
+			base.OnRenderItemText(e);
+		}
+
+		protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
+		{
+			e.ArrowColor = GetForeColor(e.Item);
+			base.OnRenderArrow(e);
+		}
+
+		protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
+		{
+			if (!(e.ToolStrip is StatusStrip))
+			{
+				base.OnRenderToolStripBorder(e);
+			}
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedSlider.Designer.cs b/editor source/Desktop/Skinning/SkinnedSlider.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fb57074c0847ddfe9a013bb9e5d383c83e729121
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedSlider.Designer.cs	
@@ -0,0 +1,45 @@
+namespace Desktop.Skinning
+{
+	partial class SkinnedSlider
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.SuspendLayout();
+			// 
+			// SkinnedSlider
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Name = "SkinnedSlider";
+			this.Size = new System.Drawing.Size(150, 21);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedSlider.cs b/editor source/Desktop/Skinning/SkinnedSlider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..95527f67fc1e447970f1dc4de4757160b4a6dec5
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedSlider.cs	
@@ -0,0 +1,304 @@
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	[DefaultEvent("ValueChanged")]
+	public partial class SkinnedSlider : UserControl, ISkinControl, ISupportInitialize
+	{
+		public event EventHandler ValueChanged;
+
+		private bool _dragging;
+
+		public SkinnedSlider()
+		{
+			InitializeComponent();
+			DoubleBuffered = true;
+			SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+			SetStyle(ControlStyles.UserMouse, true);
+			SetStyle(ControlStyles.UserPaint, true);
+			SetStyle(ControlStyles.ContainerControl, true);
+			SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
+			SetStyle(ControlStyles.Selectable, true);
+			SetStyle(ControlStyles.ResizeRedraw, true);
+			SetStyle(ControlStyles.SupportsTransparentBackColor, true);
+		}
+
+		private int _minimum = 0;
+		public int Minimum
+		{
+			get { return _minimum; }
+			set { _minimum = value; Value = _value; }
+		}
+
+		private int _maximum = 100;
+		public int Maximum
+		{
+			get { return _maximum; }
+			set { _maximum = value; Value = _value; }
+		}
+
+		private int _value = 0;
+		public int Value
+		{
+			get { return _value; }
+			set
+			{
+				value = Math.Max(_minimum, Math.Min(_maximum, value));
+				if (_value != value)
+				{
+					_value = value;
+					ValueChanged?.Invoke(this, EventArgs.Empty);
+					Invalidate(true);
+				}
+			}
+		}
+
+		public int Increment { get; set; } = 10;
+
+		private int _tickFrequency = 10;
+		public int TickFrequency
+		{
+			get { return _tickFrequency; }
+			set
+			{
+				if (_tickFrequency != value)
+				{
+					_tickFrequency = value;
+					Invalidate(true);
+				}
+			}
+		}
+
+		private SkinnedFieldType _fieldType = SkinnedFieldType.Primary;
+		public SkinnedFieldType FieldType
+		{
+			get { return _fieldType; }
+			set
+			{
+				_fieldType = value;
+				_colorSet = null;
+				Invalidate(true);
+			}
+		}
+
+		private ColorSet _colorSet;
+		private ColorSet _lightColorSet;
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			_colorSet = null;
+		}
+
+		public VisualState MouseState { get; private set; }
+
+		private const int MarkerSize = 10;
+
+		private Rectangle GetTrackRectangle()
+		{
+			return new Rectangle(ClientRectangle.X + MarkerSize, ClientRectangle.Y, ClientRectangle.Width - MarkerSize * 2, ClientRectangle.Height);
+		}
+
+		private Rectangle GetThumbRectangle(int padding)
+		{
+			Rectangle trackRect = GetTrackRectangle();
+			float pct = (Value - Minimum) / (float)(Maximum - Minimum);
+			int left = (int)(trackRect.Left + pct * trackRect.Width);
+			Rectangle thumbRect = new Rectangle(left - MarkerSize / 2 - padding, trackRect.Y + trackRect.Height / 2 - MarkerSize / 2 - padding, MarkerSize + padding * 2, MarkerSize + padding * 2);
+			return thumbRect;
+		}
+
+		/// <summary>
+		/// Moves the slider thumb to the given point
+		/// </summary>
+		/// <param name="x"></param>
+		private void MoveSlider(int x)
+		{
+			Rectangle track = GetTrackRectangle();
+			float amount = (x - track.X) / (float)track.Width;
+			Value = (int)(Minimum + amount * (Maximum - Minimum));
+			Invalidate();
+		}
+
+		protected override void OnMouseMove(MouseEventArgs e)
+		{
+			if (_dragging)
+			{
+				MoveSlider(e.X);
+			}
+			if (GetThumbRectangle(3).Contains(new Point(e.X, e.Y)))
+			{
+				MouseState = VisualState.Hover;
+				Invalidate();
+			}
+			else if (MouseState == VisualState.Hover)
+			{
+				MouseState = VisualState.Normal;
+				Invalidate();
+			}
+			base.OnMouseMove(e);
+		}
+
+		protected override void OnMouseLeave(EventArgs e)
+		{
+			MouseState = VisualState.Normal;
+			Invalidate();
+		}
+
+		protected override void OnMouseDown(MouseEventArgs e)
+		{
+			if (e.Button == MouseButtons.Left)
+			{
+				MouseState = VisualState.Pressed;
+
+				Rectangle trackRect = GetTrackRectangle();
+				Rectangle thumbRect = GetThumbRectangle(3);
+				Point mouse = new Point(e.X, e.Y);
+				if (thumbRect.Contains(mouse))
+				{
+					_dragging = true;
+				}
+				else if (trackRect.Contains(mouse))
+				{
+					if (e.X < thumbRect.X)
+					{
+						Value -= Increment;
+					}
+					else if (e.X > thumbRect.Width)
+					{
+						Value += Increment;
+					}
+				}
+
+				Invalidate();
+			}
+		}
+
+		protected override void OnMouseWheel(MouseEventArgs e)
+		{
+			base.OnMouseWheel(e);
+			if (e.Delta < 0)
+			{
+				Value -= Increment;
+			}
+			else if (e.Delta > 0)
+			{
+				Value += Increment;
+			}
+		}
+
+		protected override bool IsInputKey(Keys keyData)
+		{
+			bool isInput = base.IsInputKey(keyData);
+			if (keyData == Keys.Left || keyData == Keys.Right || keyData == Keys.Up || keyData == Keys.Down)
+			{
+				return true;
+			}
+			return isInput;
+		}
+
+		protected override void OnKeyDown(KeyEventArgs e)
+		{
+			if (e.KeyCode == Keys.Left || e.KeyCode == Keys.Down)
+			{
+				Value -= Increment;
+				e.Handled = true;
+			}
+			else if (e.KeyCode == Keys.Right || e.KeyCode == Keys.Up)
+			{
+				Value += Increment;
+				e.Handled = true;
+			}
+			base.OnKeyDown(e);
+		}
+
+		protected override void OnMouseUp(MouseEventArgs e)
+		{
+			MouseState = VisualState.Hover;
+			if (_dragging)
+			{
+				_dragging = false;
+			}
+			Invalidate();
+		}
+
+		protected override void OnLostFocus(EventArgs e)
+		{
+			base.OnLostFocus(e);
+			Invalidate();
+		}
+
+		protected override void OnPaint(PaintEventArgs e)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			if (_colorSet == null)
+			{
+				_colorSet = skin.GetFieldColorSet(FieldType, SkinnedLightLevel.Normal);
+				_lightColorSet = skin.GetFieldColorSet(FieldType, SkinnedLightLevel.Light);
+			}
+
+			Graphics g = e.Graphics;
+
+			Color backColor = DesignMode ? SystemColors.Control : this.GetSkinnedPanelBackColor();
+			g.Clear(backColor);
+
+			g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
+
+			//trackbar
+			Rectangle trackRect = new Rectangle(ClientRectangle.X + MarkerSize, ClientRectangle.Y, ClientRectangle.Width - MarkerSize * 2, ClientRectangle.Height);
+
+			using (Pen pen = new Pen(Enabled ? _lightColorSet.Normal : _lightColorSet.Disabled, 2))
+			{
+				using (Pen forePen = new Pen(DesignMode ? ForeColor : this.GetSkinnedPanelForeColor()))
+				{
+					g.DrawLine(pen, trackRect.X, trackRect.Y + trackRect.Height / 2, trackRect.Right, trackRect.Y + trackRect.Height / 2);
+
+					//tick marks
+					if (_tickFrequency > 0)
+					{
+						for (int i = Minimum; i <= Maximum; i += _tickFrequency)
+						{
+							float p = (i - Minimum) / (float)(Maximum - Minimum);
+							int tickLeft = (int)(trackRect.Left + p * trackRect.Width);
+							g.DrawLine(forePen, tickLeft, trackRect.Bottom - 5, tickLeft, trackRect.Bottom - 1);
+						}
+					}
+
+					float pct = (Value - Minimum) / (float)(Maximum - Minimum);
+					int left = (int)(trackRect.Left + pct * trackRect.Width);
+
+					//filled track
+					ColorSet thumbSet = skin.GetWidgetColorSet(FieldType);
+					pen.Color = thumbSet.GetColor(VisualState.Normal, Focused, Enabled);
+					g.DrawLine(pen, trackRect.X, trackRect.Y + trackRect.Height / 2, left, trackRect.Y + trackRect.Height / 2);
+
+					VisualState state = MouseState;
+					Rectangle thumbRect = new Rectangle(left - MarkerSize / 2, trackRect.Y + trackRect.Height / 2 - MarkerSize / 2, MarkerSize, MarkerSize);
+
+					//thumb
+					SolidBrush filledBrush = thumbSet.GetBrush(state, Focused, Enabled);
+					g.FillEllipse(filledBrush, thumbRect);
+				}
+			}
+
+			g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
+
+			if (Focused)
+			{
+				SkinManager.Instance.DrawFocusRectangle(g, ClientRectangle);
+			}
+		}
+
+		public void BeginInit()
+		{
+			
+		}
+
+		public void EndInit()
+		{
+			
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedSplitContainer.cs b/editor source/Desktop/Skinning/SkinnedSplitContainer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..760569efdaa24985ea3031364ffcb2e77fea74a0
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedSplitContainer.cs	
@@ -0,0 +1,39 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedSplitContainer : SplitContainer, ISkinControl
+	{
+		public SkinnedBackgroundType SplitterColor { get; set; } = SkinnedBackgroundType.Primary;
+
+		private SolidBrush _splitterBrush = new SolidBrush(Color.White);
+
+		protected override void OnCreateControl()
+		{
+			base.OnCreateControl();
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			_splitterBrush.Color = skin.GetBackColor(SplitterColor);
+		}
+
+		protected override void OnPaint(PaintEventArgs e)
+		{
+			base.OnPaint(e);
+
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			Graphics g = e.Graphics;
+
+			DrawSplitter(g, skin);
+		}
+
+		private void DrawSplitter(Graphics g, Skin skin)
+		{
+			g.FillRectangle(_splitterBrush, SplitterRectangle);
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedTabControl.cs b/editor source/Desktop/Skinning/SkinnedTabControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..905da5757d5e479870857931d55349f30d484f58
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedTabControl.cs	
@@ -0,0 +1,33 @@
+using System;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedTabControl : TabControl, ISkinControl
+	{
+		/// <summary>
+		/// Hide the tabs since we'll draw our own
+		/// </summary>
+		/// <param name="m"></param>
+		protected override void WndProc(ref Message m)
+		{
+			if (m.Msg == 0x1328 && !DesignMode) m.Result = (IntPtr)1;
+			else base.WndProc(ref m);
+		}
+
+		protected override void OnControlAdded(ControlEventArgs e)
+		{
+			TabPage page = e.Control as TabPage;
+			if (page != null)
+			{
+				page.BackColor = SkinManager.Instance.CurrentSkin.Background.Normal;
+			}
+			base.OnControlAdded(e);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/SkinnedTabStrip.cs b/editor source/Desktop/Skinning/SkinnedTabStrip.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ee2218f48d1110a7932664360e884f55ea4f9d8e
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedTabStrip.cs	
@@ -0,0 +1,517 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedTabStrip : Control, ISkinControl, ISkinnedPanel
+	{
+		private const int IndicatorSize = 5;
+		private const int VerticalBarWidth = 5;
+		private const int CloseButtonWidth = 20;
+		private const int CloseMarkWidth = 10;
+		private const int SpacerSize = 15;
+
+		public event EventHandler CloseButtonClicked;
+		public event EventHandler AddButtonClicked;
+
+		private int _startMargin = 5;
+		public int StartMargin
+		{
+			get { return _startMargin; }
+			set
+			{
+				_startMargin = value;
+				_tabRects = null;
+				Invalidate(true);
+			}
+		}
+
+
+		private SkinnedBackgroundType _type = SkinnedBackgroundType.PrimaryLight;
+		public SkinnedBackgroundType PanelType
+		{
+			get { return _type; }
+			set
+			{
+				_type = value;
+				Invalidate(true);
+			}
+		}
+
+		private SkinnedBackgroundType _tabType = SkinnedBackgroundType.Background;
+		public SkinnedBackgroundType TabType
+		{
+			get { return _tabType; }
+			set
+			{
+				_tabType = value;
+				Invalidate(true);
+			}
+		}
+
+		private bool _showClose;
+		public bool ShowCloseButton
+		{
+			get { return _showClose; }
+			set { _showClose = value; _tabRects = null; Invalidate(); }
+		}
+
+		private bool _showAdd;
+		public bool ShowAddButton
+		{
+			get { return _showAdd; }
+			set { _showAdd = value; _tabRects = null; Invalidate(); }
+		}
+
+		private string _addCaption;
+		public string AddCaption
+		{
+			get { return _addCaption; }
+			set { _addCaption = value; _tabRects = null; Invalidate(); }
+		}
+
+		private bool _vertical;
+		public bool Vertical
+		{
+			get { return _vertical; }
+			set { _vertical = value; Invalidate(); }
+		}
+
+		private int _tabSize = 100;
+		public int TabSize
+		{
+			get { return Vertical ? 25 : _tabSize; }
+			set { _tabSize = value; _tabRects = null; Invalidate(); }
+		}
+
+		private int _tabPadding = 20;
+		public int TabPadding
+		{
+			get { return _tabPadding; }
+			set { _tabPadding = value; _tabRects = null; Invalidate(); }
+		}
+
+		private int _tabMargin = 5;
+		public int TabMargin
+		{
+			get { return _tabMargin; }
+			set { _tabMargin = value; _tabRects = null; Invalidate(); }
+		}
+
+		private SkinnedTabControl _tabControl;
+		public SkinnedTabControl TabControl
+		{
+			get { return _tabControl; }
+			set
+			{
+				_tabControl = value;
+				if (_tabControl == null)
+				{
+					return;
+				}
+
+				_previousSelectedTabIndex = _tabControl.SelectedIndex;
+				_tabControl.Deselected += (sender, args) =>
+				{
+					_previousSelectedTabIndex = _tabControl.SelectedIndex;
+				};
+				_tabControl.SelectedIndexChanged += (sender, args) =>
+				{
+					Invalidate();
+				};
+				_tabControl.ControlAdded += delegate
+				{
+					_tabRects = null;
+					OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+					Invalidate();
+				};
+				_tabControl.ControlRemoved += delegate
+				{
+					_tabRects = null;
+					OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+					Invalidate();
+				};
+				_tabControl.VisibleChanged += delegate
+				{
+					OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+				};
+			}
+		}
+
+		private int _previousSelectedTabIndex;
+		private int _hoveredTabIndex = -1;
+		private List<Rectangle> _tabRects = null;
+
+		public SkinnedTabStrip()
+		{
+			SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.DoubleBuffer, true);
+			Margin = new Padding(0);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			if (_tabControl != null)
+			{
+				ColorSet set = skin.GetColorSet(TabType);
+				foreach (TabPage page in _tabControl.TabPages)
+				{
+					page.BackColor = set.GetColor(VisualState.Normal, false, Enabled);
+					page.ForeColor = set.ForeColor;
+				}
+			}
+		}
+
+		private void CalculateTabRects()
+		{
+			_tabRects = new List<Rectangle>();
+
+			if (_tabControl == null || _tabControl.TabCount == 0)
+			{
+				return;
+			}
+
+			if (Vertical)
+			{
+				int y = StartMargin;
+				for (int i = 0; i < _tabControl.TabPages.Count; i++)
+				{
+					Rectangle rect = new Rectangle(0, y, ClientRectangle.Width, TabSize);
+					_tabRects.Add(rect);
+					if (_tabControl.TabPages[i]?.Tag?.ToString() == "spacer")
+					{
+						y += SpacerSize;
+					}
+					else
+					{
+						y += TabSize + TabMargin;
+					}
+				}
+				if (_showAdd)
+				{
+					Rectangle rect = new Rectangle(0, y, ClientRectangle.Width, TabSize);
+					_tabRects.Add(rect);
+				}
+			}
+			else
+			{
+				using (Graphics g = CreateGraphics())
+				{
+					int x = StartMargin;
+					for (int i = 0; i < _tabControl.TabPages.Count; i++)
+					{
+						int width = TabSize;
+						if (width == -1)
+						{
+							width = (int)g.MeasureString(_tabControl.TabPages[i].Text, Skin.TabFont).Width + _tabPadding * 2;
+						}
+
+						if (ShowCloseButton || (ShowAddButton && i > 0))
+						{
+							width += CloseButtonWidth;
+						}
+						Rectangle rect = new Rectangle(x, 0, width, ClientRectangle.Height - 1);
+						_tabRects.Add(rect);
+						if (_tabControl.TabPages[i]?.Tag?.ToString() == "spacer")
+						{
+							x += SpacerSize;
+						}
+						else
+						{
+							x += width + TabMargin;
+						}
+					}
+					if (_showAdd)
+					{
+						int width = TabSize;
+						if (width == -1)
+						{
+							width = (int)g.MeasureString("Add", Skin.TabFont).Width + _tabPadding * 2 + Properties.Resources.Add.Width;
+						}
+						Rectangle rect = new Rectangle(x, 0, width, ClientRectangle.Height - 1);
+						_tabRects.Add(rect);
+					}
+				}
+			}
+		}
+
+		private List<Rectangle> GetTabRects()
+		{
+			if (_tabRects == null)
+			{
+				CalculateTabRects();
+			}
+			return _tabRects;
+		}
+
+		private ColorSet GetIndicator()
+		{
+			if (TabType == SkinnedBackgroundType.Secondary)
+			{
+				return SkinManager.Instance.CurrentSkin.PrimaryColor;
+			}
+			else
+			{
+				return SkinManager.Instance.CurrentSkin.SecondaryColor;
+			}
+		}
+
+		protected override void OnPaint(PaintEventArgs e)
+		{
+			if (Vertical)
+			{
+				PaintVertical(e);
+			}
+			else
+			{
+				PaintHorizontal(e);
+			}
+		}
+
+		private void PaintHorizontal(PaintEventArgs e)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			Graphics g = e.Graphics;
+
+			ColorSet set = skin.GetColorSet(PanelType);
+			Color color = set.GetColor(VisualState.Normal, false, Enabled);
+			g.Clear(color);
+
+			ColorSet tabSet = skin.GetColorSet(TabType);
+
+			if (_tabControl == null) { return; }
+
+			SolidBrush indicatorBrush = GetIndicator().GetBrush(VisualState.Normal, false, Enabled);
+			SolidBrush hoverBrush = set.GetBrush(VisualState.Hover, false, Enabled);
+			Pen borderPen = set.GetBorderPen(VisualState.Normal, false, Enabled);
+
+			List<Rectangle> rects = GetTabRects();
+			g.DrawLine(borderPen, ClientRectangle.X, ClientRectangle.Bottom - 1, ClientRectangle.Right, ClientRectangle.Bottom - 1);
+			using (StringFormat sf = new StringFormat() { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center, FormatFlags = StringFormatFlags.NoWrap, Trimming = StringTrimming.EllipsisCharacter })
+			{
+				using (Brush activeBrush = new SolidBrush(Enabled ? set.ForeColor : set.DisabledForeColor))
+				{
+					using (Brush textBrush = new SolidBrush(Enabled ? set.ForeColor : set.DisabledForeColor))
+					{
+						for (int i = 0; i < rects.Count; i++)
+						{
+							bool showClose = ShowCloseButton || (i > 0 && ShowAddButton && i < rects.Count - 1);
+							bool showAdd = (ShowAddButton && i == rects.Count - 1);
+							Rectangle rect = rects[i];
+							TabPage page = i < _tabControl.TabPages.Count ? _tabControl.TabPages[i] : null;
+							string text = page == null ? AddCaption : page.Text;
+							int buttonWidth = showClose ? CloseButtonWidth : 0;
+							Rectangle textRect = new Rectangle(rect.X, rect.Y + IndicatorSize + 1, rect.Width - buttonWidth, rect.Height - IndicatorSize - 2);
+							if (showAdd)
+							{
+								textRect = new Rectangle(rect.X + Properties.Resources.Add.Width, rect.Y + IndicatorSize + 1, rect.Width - Properties.Resources.Add.Width, textRect.Height);
+							}
+
+							if (_hoveredTabIndex == i)
+							{
+								g.FillRectangle(hoverBrush, rect);
+							}
+
+							//tab and text
+							if (i == _tabControl.SelectedIndex)
+							{
+								SolidBrush tabBrush = tabSet.GetBrush(VisualState.Normal, false, Enabled);
+								g.FillRectangle(tabBrush, rect.X, rect.Y, rect.Width, rect.Height + 1); //Height + 1 to cover border line
+								g.FillRectangle(indicatorBrush, new RectangleF(rect.X, 1, rect.Width, IndicatorSize));
+								g.DrawLine(borderPen, rect.X, rect.Top, rect.Right, rect.Top);
+								g.DrawLine(borderPen, rect.X, rect.Y + 1, rect.X, rect.Bottom);
+								g.DrawLine(borderPen, rect.Right, rect.Y + 1, rect.Right, rect.Bottom);
+
+								using (SolidBrush foreBrush = new SolidBrush(Enabled ? tabSet.ForeColor : tabSet.DisabledForeColor))
+								{
+									g.DrawString(text, Skin.ActiveTabFont, foreBrush, textRect, sf);
+
+									if (showClose && !showAdd)
+									{
+										//close button
+										g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
+										Color closeColor = skin.PrimaryForeColor;
+										Rectangle closeRect = GetCloseRectangle();
+										if (!RectangleToScreen(new Rectangle(closeRect.X - 3, closeRect.Y - 3, closeRect.Width + 6, closeRect.Height + 6)).Contains(MousePosition))
+										{
+											closeColor = Color.FromArgb(127, closeColor);
+										}
+										using (Pen closePen = new Pen(closeColor, 2))
+										{
+											g.DrawLine(closePen, closeRect.X, closeRect.Y, closeRect.Right, closeRect.Bottom);
+											g.DrawLine(closePen, closeRect.X, closeRect.Bottom, closeRect.Right, closeRect.Top);
+										}
+										g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
+									}
+								}
+							}
+							else
+							{
+								if (showAdd)
+								{
+									//Add buttton
+									g.DrawImage(Properties.Resources.Add, textRect.X - Properties.Resources.Add.Width + TabPadding, textRect.Y + 1);
+								}
+
+								g.DrawString(text, Skin.TabFont, textBrush, textRect, sf);
+							}
+						}
+					}
+				}
+			}
+		}
+
+		private Rectangle GetCloseRectangle()
+		{
+			Rectangle rect = _tabRects[_tabControl.SelectedIndex];
+			Rectangle textRect = new Rectangle(rect.X, rect.Y + IndicatorSize + 1, rect.Width - CloseButtonWidth, rect.Height - IndicatorSize - 2);
+			int crossX = rect.Right - CloseButtonWidth;
+			int crossY = textRect.Top + textRect.Height / 2 - CloseMarkWidth / 2;
+			Rectangle closeRect = new Rectangle(crossX, crossY, CloseMarkWidth, CloseMarkWidth);
+			return closeRect;
+		}
+
+		private void PaintVertical(PaintEventArgs e)
+		{
+			Graphics g = e.Graphics;
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
+			ColorSet background = skin.GetColorSet(PanelType);
+			ColorSet tabSet = skin.GetColorSet(TabType);
+
+			Color backColor = background.GetColor(VisualState.Normal, false, Enabled);
+			g.Clear(backColor);
+
+			if (_tabControl == null) { return; }
+
+			Brush indicatorBrush = tabSet.GetBrush(VisualState.Normal, false, Enabled);
+			Brush hoverBrush = background.GetBrush(VisualState.Hover, false, Enabled);
+			Pen innerBorderPen = background.GetBorderPen(VisualState.Normal, false, Enabled);
+			g.DrawLine(innerBorderPen, e.ClipRectangle.Right - 1, 0, e.ClipRectangle.Right - 1, e.ClipRectangle.Height);
+
+			List<Rectangle> rects = GetTabRects();
+
+			using (StringFormat sf = new StringFormat() { LineAlignment = StringAlignment.Center, FormatFlags = StringFormatFlags.NoWrap, Trimming = StringTrimming.EllipsisCharacter })
+			{
+				using (Brush activeBrush = new SolidBrush(Enabled ? tabSet.ForeColor : tabSet.DisabledForeColor))
+				{
+					using (Brush textBrush = new SolidBrush(Enabled ? background.ForeColor : background.DisabledForeColor))
+					{
+						for (int i = 0; i < _tabControl.TabPages.Count; i++)
+						{
+							Rectangle rect = rects[i];
+							TabPage page = _tabControl.TabPages[i];
+							string text = page.Text;
+
+							Rectangle textRect = new Rectangle(rect.X + 3, rect.Y, rect.Width - 6, rect.Height);
+
+							if (_tabControl.SelectedIndex == i)
+							{
+								Rectangle clientRect = ClientRectangle;
+								Brush activeBar = skin.SecondaryColor.GetBrush(VisualState.Normal, false, Enabled);
+								g.FillRectangle(indicatorBrush, rect);
+								g.FillRectangle(activeBar, new RectangleF(rect.X + 1, rect.Y, IndicatorSize, rect.Height));
+								g.DrawString(text, Skin.ActiveTabFont, activeBrush, new Rectangle(textRect.X + IndicatorSize, textRect.Y, textRect.Width - IndicatorSize, textRect.Height), sf);
+								g.DrawLine(innerBorderPen, clientRect.X, textRect.Y - 1, clientRect.X, textRect.Bottom);
+								g.DrawLine(innerBorderPen, clientRect.X, textRect.Y - 1, clientRect.Right - 2, textRect.Y - 1);
+								g.DrawLine(innerBorderPen, clientRect.X, textRect.Bottom, clientRect.Right - 2, textRect.Bottom);
+							}
+							else if (_hoveredTabIndex == i)
+							{
+								g.FillRectangle(hoverBrush, new RectangleF(rect.X, rect.Y, rect.Width - 1, rect.Height));
+								g.DrawString(text, Skin.TabFont, textBrush, textRect, sf);
+							}
+							else
+							{
+								g.DrawString(text, Skin.TabFont, textBrush, textRect, sf);
+							}
+						}
+					}
+				}
+			}
+		}
+
+		protected override void OnMouseLeave(EventArgs e)
+		{
+			base.OnMouseLeave(e);
+
+			_hoveredTabIndex = -1;
+			Invalidate();
+		}
+
+		protected override void OnMouseMove(MouseEventArgs e)
+		{
+			base.OnMouseMove(e);
+
+			List<Rectangle> rects = GetTabRects();
+			Point pt = new Point(e.X, e.Y);
+			for (int i = 0; i < rects.Count; i++)
+			{
+				Rectangle rect = rects[i];
+				if (rect.Contains(pt))
+				{
+					TabPage page = i < _tabControl.TabPages.Count ? _tabControl.TabPages[i] : null;
+					if (page?.Tag?.ToString() == "spacer")
+					{
+						continue;
+					}
+					_hoveredTabIndex = i;
+					Invalidate();
+					return;
+				}
+			}
+			if (_hoveredTabIndex != -1)
+			{
+				_hoveredTabIndex = -1;
+				Invalidate();
+			}
+		}
+
+		protected override void OnMouseClick(MouseEventArgs e)
+		{
+			List<Rectangle> rects = GetTabRects();
+			Point pt = new Point(e.X, e.Y);
+
+			if ((ShowCloseButton || (_showAdd && _tabControl.SelectedIndex > 0)) && _tabControl.SelectedIndex >= 0)
+			{
+				Rectangle closeRect = GetCloseRectangle();
+				closeRect.X -= 3;
+				closeRect.Y -= 3;
+				closeRect.Height += 6;
+				closeRect.Width += 6;
+				if (closeRect.Contains(pt))
+				{
+					CloseButtonClicked?.Invoke(this, EventArgs.Empty);
+					return;
+				}
+			}
+
+			for (int i = 0; i < rects.Count; i++)
+			{
+				TabPage page = i < _tabControl.TabPages.Count ? _tabControl.TabPages[i]: null;
+				if (page?.Tag?.ToString() == "spacer")
+				{
+					continue;
+				}
+				Rectangle rect = rects[i];
+				if (rect.Contains(pt))
+				{
+					if (i >= _tabControl.TabPages.Count)
+					{
+						if (ShowAddButton)
+						{
+							//add tab clicked
+							AddButtonClicked?.Invoke(this, EventArgs.Empty);
+						}
+					}
+					else
+					{
+						_tabControl.SelectedIndex = i;
+					}
+					break;
+				}
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/editor source/Desktop/Skinning/SkinnedTextBox.cs b/editor source/Desktop/Skinning/SkinnedTextBox.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3de0c912165229fae810b03f13de5bfe8ba905d2
--- /dev/null
+++ b/editor source/Desktop/Skinning/SkinnedTextBox.cs	
@@ -0,0 +1,18 @@
+using System.Windows.Forms;
+
+namespace Desktop.Skinning
+{
+	public class SkinnedTextBox : TextBox, ISkinControl
+	{
+		protected override void OnCreateControl()
+		{
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			ForeColor = skin.Surface.ForeColor;
+			BackColor = skin.FieldBackColor;
+		}
+	}
+}
diff --git a/editor source/Desktop/Skinning/VisualState.cs b/editor source/Desktop/Skinning/VisualState.cs
new file mode 100644
index 0000000000000000000000000000000000000000..44bca5b16eff5113e68459674d18011fe1a2500c
--- /dev/null
+++ b/editor source/Desktop/Skinning/VisualState.cs	
@@ -0,0 +1,24 @@
+namespace Desktop.Skinning
+{
+	public enum VisualState
+	{
+		/// <summary>
+		/// Normal state
+		/// </summary>
+		Normal = 0,
+		/// <summary>
+		/// Pointer is hovering over element
+		/// </summary>
+		Hover = 1,
+		/// <summary>
+		/// Element is being pressed
+		/// </summary>
+		Pressed = 2,
+		/// <summary>
+		/// Element is focused
+		/// </summary>
+		Focused = 3,
+		Disabled = 4,
+		DisabledSelected = 5,
+	}
+}
diff --git a/editor source/KisekaeImporter/DataStructures/Kisekae/KisekaeSubCodeMap.cs b/editor source/KisekaeImporter/DataStructures/Kisekae/KisekaeSubCodeMap.cs
index b897990f5f0aec26a6cd9effde8c3454c9ee96d2..33a6ed6009aee79aa0a62cd333c18cfd3c7f4900 100644
--- a/editor source/KisekaeImporter/DataStructures/Kisekae/KisekaeSubCodeMap.cs	
+++ b/editor source/KisekaeImporter/DataStructures/Kisekae/KisekaeSubCodeMap.cs	
@@ -55,6 +55,7 @@ namespace KisekaeImporter
 				case "fe":
 				case "ff":
 				case "fg":
+				case "fk":
 				case "fi":
 				case "pa":
 				case "t":
diff --git a/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeJacket.cs b/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeJacket.cs
index 8f9598de85581a4ded537f657e22c40cad88c143..b3c0e2e7f88ea297633bd847d8b3324d993c0f26 100644
--- a/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeJacket.cs	
+++ b/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeJacket.cs	
@@ -44,6 +44,66 @@
 			set { Set(10, value.ToString()); }
 		}
 
+		public int Pocket
+		{
+			get { return GetInt(11); }
+			set { Set(11, value.ToString()); }
+		}
+
+		public KisekaeColor PocketColor1
+		{
+			get { return new KisekaeColor(GetString(12)); }
+			set { Set(12, value.ToString()); }
+		}
+
+		public KisekaeColor PocketColor2
+		{
+			get { return new KisekaeColor(GetString(13)); }
+			set { Set(13, value.ToString()); }
+		}
+
+		public KisekaeColor PocketColor3
+		{
+			get { return new KisekaeColor(GetString(14)); }
+			set { Set(14, value.ToString()); }
+		}
+
+		public int PocketSide
+		{
+			get { return GetInt(15); }
+			set { Set(15, value.ToString()); }
+		}
+
+		public int LowerPocket
+		{
+			get { return GetInt(16); }
+			set { Set(16, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor1
+		{
+			get { return new KisekaeColor(GetString(17)); }
+			set { Set(17, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor2
+		{
+			get { return new KisekaeColor(GetString(18)); }
+			set { Set(18, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor3
+		{
+			get { return new KisekaeColor(GetString(19)); }
+			set { Set(19, value.ToString()); }
+		}
+
+		public int LowerPocketRotation
+		{
+			get { return GetInt(20); }
+			set { Set(20, value.ToString()); }
+		}
+
 		public void SetOpenState(int state)
 		{
 			OpenState = state;
diff --git a/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeShirt.cs b/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeShirt.cs
index 9cfd6180887f6fa9b28503ef33e7345e25fbd0a5..19ae90c56418f7af424428d158dd41d9d2b7e302 100644
--- a/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeShirt.cs	
+++ b/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeShirt.cs	
@@ -92,5 +92,77 @@
 			get { return GetInt(14); }
 			set { Set(14, value.ToString()); }
 		}
+
+		public int Pocket
+		{
+			get { return GetInt(15); }
+			set { Set(15, value.ToString()); }
+		}
+
+		public KisekaeColor PocketColor1
+		{
+			get { return new KisekaeColor(GetString(16)); }
+			set { Set(16, value.ToString()); }
+		}
+
+		public KisekaeColor PocketColor2
+		{
+			get { return new KisekaeColor(GetString(17)); }
+			set { Set(17, value.ToString()); }
+		}
+
+		public KisekaeColor PocketColor3
+		{
+			get { return new KisekaeColor(GetString(18)); }
+			set { Set(18, value.ToString()); }
+		}
+
+		public int PocketSide
+		{
+			get { return GetInt(19); }
+			set { Set(19, value.ToString()); }
+		}
+		
+		public int CollarStyle
+		{
+			get { return GetInt(20); }
+			set { Set(20, value.ToString()); }
+		}
+
+		public int CollarPattern
+		{
+			get { return GetInt(21); }
+			set { Set(21, value.ToString()); }
+		}
+
+		public int LowerPocket
+		{
+			get { return GetInt(22); }
+			set { Set(22, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor1
+		{
+			get { return new KisekaeColor(GetString(23)); }
+			set { Set(23, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor2
+		{
+			get { return new KisekaeColor(GetString(24)); }
+			set { Set(24, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor3
+		{
+			get { return new KisekaeColor(GetString(25)); }
+			set { Set(25, value.ToString()); }
+		}
+
+		public int LowerPocketRotation
+		{
+			get { return GetInt(26); }
+			set { Set(26, value.ToString()); }
+		}
 	}
 }
diff --git a/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeSweater.cs b/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeSweater.cs
index d0113a846e6460902d6abcf8870e7a1b8833c4a3..b38ec85c1ca1ad1c3b7b0ce2e6015dd0cfeef2b9 100644
--- a/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeSweater.cs	
+++ b/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeSweater.cs	
@@ -110,5 +110,35 @@
 			get { return GetInt(18); }
 			set { Set(18, value.ToString()); }
 		}
+
+		public int LowerPocket
+		{
+			get { return GetInt(19); }
+			set { Set(19, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor1
+		{
+			get { return new KisekaeColor(GetString(20)); }
+			set { Set(20, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor2
+		{
+			get { return new KisekaeColor(GetString(21)); }
+			set { Set(21, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor3
+		{
+			get { return new KisekaeColor(GetString(22)); }
+			set { Set(22, value.ToString()); }
+		}
+
+		public int LowerPocketRotation
+		{
+			get { return GetInt(23); }
+			set { Set(23, value.ToString()); }
+		}
 	}
 }
diff --git a/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeUndershirt.cs b/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeUndershirt.cs
index b876c3648949e3fdeb18fcd4f3876896b33b1df6..0ed565990f0c5ec6ea6683e97cc7492d721c3e60 100644
--- a/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeUndershirt.cs	
+++ b/editor source/KisekaeImporter/DataStructures/Kisekae/SubCodes/KisekaeUndershirt.cs	
@@ -91,5 +91,35 @@
 			get { return GetInt(18); }
 			set { Set(18, value.ToString()); }
 		}
+
+		public int LowerPocket
+		{
+			get { return GetInt(19); }
+			set { Set(19, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor1
+		{
+			get { return new KisekaeColor(GetString(20)); }
+			set { Set(20, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor2
+		{
+			get { return new KisekaeColor(GetString(21)); }
+			set { Set(21, value.ToString()); }
+		}
+
+		public KisekaeColor LowerPocketColor3
+		{
+			get { return new KisekaeColor(GetString(22)); }
+			set { Set(22, value.ToString()); }
+		}
+
+		public int LowerPocketRotation
+		{
+			get { return GetInt(23); }
+			set { Set(23, value.ToString()); }
+		}
 	}
 }
diff --git a/editor source/KisekaeImporter/ImageImport/ImageMetadata.cs b/editor source/KisekaeImporter/ImageImport/ImageMetadata.cs
index 747fffa2e9a8e88c32dfdf6aa771083c26362b5e..397ede5d94d5358a1c1484194289c9832c4ca3fd 100644
--- a/editor source/KisekaeImporter/ImageImport/ImageMetadata.cs	
+++ b/editor source/KisekaeImporter/ImageImport/ImageMetadata.cs	
@@ -38,6 +38,11 @@ namespace KisekaeImporter.ImageImport
 			return Regex.IsMatch(Data, @"^\d*\*\*.*");
 		}
 
+		public override string ToString()
+		{
+			return ImageKey;
+		}
+
 		public string Serialize()
 		{
 			List<string> attributes = new List<string>();
diff --git a/editor source/SPNATI Character Editor/About.Designer.cs b/editor source/SPNATI Character Editor/About.Designer.cs
index a7588e13834d28c3476850ef4b6a3137a033eaee..367f0b55e5ecfdb58cb3d015e5498ff66ffbf698 100644
--- a/editor source/SPNATI Character Editor/About.Designer.cs	
+++ b/editor source/SPNATI Character Editor/About.Designer.cs	
@@ -28,14 +28,19 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.lblVersion = new System.Windows.Forms.Label();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.lblVersion = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(197, 35);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.Location = new System.Drawing.Point(206, 4);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 0;
@@ -46,25 +51,38 @@
 			// lblVersion
 			// 
 			this.lblVersion.AutoSize = true;
-			this.lblVersion.Location = new System.Drawing.Point(13, 13);
+			this.lblVersion.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblVersion.Location = new System.Drawing.Point(12, 39);
 			this.lblVersion.Name = "lblVersion";
 			this.lblVersion.Size = new System.Drawing.Size(149, 13);
 			this.lblVersion.TabIndex = 1;
 			this.lblVersion.Text = "SPNATI Character Editor v1.0";
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 64);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(284, 30);
+			this.skinnedPanel1.TabIndex = 2;
+			// 
 			// About
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.ClientSize = new System.Drawing.Size(284, 70);
+			this.ClientSize = new System.Drawing.Size(284, 94);
+			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.lblVersion);
-			this.Controls.Add(this.cmdOK);
 			this.Name = "About";
 			this.ShowIcon = false;
 			this.ShowInTaskbar = false;
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "About";
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -72,7 +90,8 @@
 
 		#endregion
 
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Label lblVersion;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedLabel lblVersion;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/About.cs b/editor source/SPNATI Character Editor/About.cs
index ec6915fb978f1fa2c99556d177b9e7ed27a4635d..52ba8976ca61570692bf6716ecef9bc7b10ed23d 100644
--- a/editor source/SPNATI Character Editor/About.cs	
+++ b/editor source/SPNATI Character Editor/About.cs	
@@ -1,4 +1,5 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor
@@ -6,7 +7,7 @@ namespace SPNATI_Character_Editor
 	/// <summary>
 	/// About form
 	/// </summary>
-	public partial class About : Form
+	public partial class About : SkinnedForm
 	{
 		public About()
 		{
diff --git a/editor source/SPNATI Character Editor/Actions/DeleteAnimatedPropertyCommand.cs b/editor source/SPNATI Character Editor/Actions/DeleteAnimatedPropertyCommand.cs
index 5e45ae81f7760b50b835e278b42119ff17e5b53f..b6a9d92431e9a0fa71b8fb8ea50123533ba982a3 100644
--- a/editor source/SPNATI Character Editor/Actions/DeleteAnimatedPropertyCommand.cs	
+++ b/editor source/SPNATI Character Editor/Actions/DeleteAnimatedPropertyCommand.cs	
@@ -9,24 +9,24 @@ namespace SPNATI_Character_Editor.Actions
 	/// </summary>
 	public class DeleteAnimatedPropertyCommand : ICommand
 	{
-		private LiveSprite _sprite;
+		private LiveAnimatedObject _object;
 		private string _property;
 		private List<ICommand> _commands = new List<ICommand>();
 
-		public DeleteAnimatedPropertyCommand(LiveSprite sprite, string property)
+		public DeleteAnimatedPropertyCommand(LiveAnimatedObject obj, string property)
 		{
-			_sprite = sprite;
+			_object = obj;
 			_property = property;
 		}
 
 		public void Do()
 		{
 			_commands.Clear();
-			for (int i = _sprite.Keyframes.Count - 1; i >= 0; i--)
+			for (int i = _object.Keyframes.Count - 1; i >= 0; i--)
 			{
-				if (_sprite.Keyframes[i].HasProperty(_property))
+				if (_object.Keyframes[i].HasProperty(_property))
 				{
-					DeletePropertyCommand command = new DeletePropertyCommand(_sprite, _sprite.Keyframes[i], _property);
+					DeletePropertyCommand command = new DeletePropertyCommand(_object, _object.Keyframes[i], _property);
 					command.Do();
 					_commands.Add(command);
 				}
diff --git a/editor source/SPNATI Character Editor/Actions/DeleteKeyframeCommand.cs b/editor source/SPNATI Character Editor/Actions/DeleteKeyframeCommand.cs
index d41c6ab2906ca39feb33804b0b499d3aafe5d521..5d47102c39cceba096c10fc500e7bf74465cb4f2 100644
--- a/editor source/SPNATI Character Editor/Actions/DeleteKeyframeCommand.cs	
+++ b/editor source/SPNATI Character Editor/Actions/DeleteKeyframeCommand.cs	
@@ -8,23 +8,23 @@ namespace SPNATI_Character_Editor.Actions
 	/// </summary>
 	public class DeleteKeyframeCommand : ICommand
 	{
-		private LiveSprite _sprite;
+		private LiveAnimatedObject _data;
 		private LiveKeyframe _keyframe;
 
-		public DeleteKeyframeCommand(LiveSprite sprite, LiveKeyframe keyframe)
+		public DeleteKeyframeCommand(LiveAnimatedObject data, LiveKeyframe keyframe)
 		{
-			_sprite = sprite;
+			_data = data;
 			_keyframe = keyframe;
 		}
 
 		public void Do()
 		{
-			_sprite.RemoveKeyframe(_keyframe);
+			_data.RemoveKeyframe(_keyframe);
 		}
 
 		public void Undo()
 		{
-			_sprite.AddKeyframe(_keyframe);
+			_data.AddKeyframe(_keyframe);
 		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Actions/DeletePropertyCommand.cs b/editor source/SPNATI Character Editor/Actions/DeletePropertyCommand.cs
index 4c757715c0b366f1f06886cf77739396dfda62a2..8acf840cdc95114c7ce5a6d04c517170903b7601 100644
--- a/editor source/SPNATI Character Editor/Actions/DeletePropertyCommand.cs	
+++ b/editor source/SPNATI Character Editor/Actions/DeletePropertyCommand.cs	
@@ -8,14 +8,14 @@ namespace SPNATI_Character_Editor.Actions
 	/// </summary>
 	public class DeletePropertyCommand : ICommand
 	{
-		private LiveSprite _sprite;
+		private LiveAnimatedObject _object;
 		private LiveKeyframe _keyframe;
 		private string _property;
 		private object _value;
 
-		public DeletePropertyCommand(LiveSprite sprite, LiveKeyframe keyframe, string property)
+		public DeletePropertyCommand(LiveAnimatedObject obj, LiveKeyframe keyframe, string property)
 		{
-			_sprite = sprite;
+			_object = obj;
 			_keyframe = keyframe;
 			_property = property;
 			_value = keyframe.Get<object>(property);
@@ -31,9 +31,9 @@ namespace SPNATI_Character_Editor.Actions
 			_keyframe.Set(_value, _property);
 
 			//if deleting the property wiped out the keyframe, add it back
-			if (!_sprite.Keyframes.Contains(_keyframe))
+			if (!_object.Keyframes.Contains(_keyframe))
 			{
-				_sprite.AddKeyframe(_keyframe);
+				_object.AddKeyframe(_keyframe);
 			}
 		}
 	}
diff --git a/editor source/SPNATI Character Editor/Actions/DeleteWidgetCommand.cs b/editor source/SPNATI Character Editor/Actions/DeleteWidgetCommand.cs
index e7fc9958c468eafb06ebd65cc144b2fa090e0802..02ba4835f9e22091984093256a5fe48fe4d0a0b7 100644
--- a/editor source/SPNATI Character Editor/Actions/DeleteWidgetCommand.cs	
+++ b/editor source/SPNATI Character Editor/Actions/DeleteWidgetCommand.cs	
@@ -5,11 +5,11 @@ namespace SPNATI_Character_Editor.Actions
 {
 	public class DeleteWidgetCommand : ICommand
 	{
-		private ITimelineWidget _widget;
+		private ITimelineObject _widget;
 		private ITimelineData _data;
 		private int _index;
 
-		public DeleteWidgetCommand(ITimelineData data, ITimelineWidget widget)
+		public DeleteWidgetCommand(ITimelineData data, ITimelineObject widget)
 		{
 			_widget = widget;
 			_data = data;
diff --git a/editor source/SPNATI Character Editor/Actions/MoveSpriteStartCommand.cs b/editor source/SPNATI Character Editor/Actions/MoveSpriteStartCommand.cs
index ab899b44014109f515e03001b636849254ff5188..d56aad4f6525d286a77dd71854f99a99129c8004 100644
--- a/editor source/SPNATI Character Editor/Actions/MoveSpriteStartCommand.cs	
+++ b/editor source/SPNATI Character Editor/Actions/MoveSpriteStartCommand.cs	
@@ -10,7 +10,7 @@ namespace SPNATI_Character_Editor.Actions
 	/// </summary>
 	public class MoveSpriteStartCommand : ITimeCommand
 	{
-		private LiveSprite _sprite;
+		private LiveAnimatedObject _data;
 		private float _oldStart;
 		private float _oldLength;
 		public float Time;
@@ -19,16 +19,16 @@ namespace SPNATI_Character_Editor.Actions
 
 		public LiveKeyframe NewKeyframe
 		{
-			get { return _sprite.Keyframes[0]; }
+			get { return _data.Keyframes[0]; }
 		}
 
-		public MoveSpriteStartCommand(LiveSprite sprite, float start)
+		public MoveSpriteStartCommand(LiveAnimatedObject data, float start)
 		{
-			_sprite = sprite;
+			_data = data;
 			Time = start;
-			_oldStart = _sprite.Start;
-			_oldLength = _sprite.Length;
-			foreach (LiveKeyframe kf in sprite.Keyframes)
+			_oldStart = _data.Start;
+			_oldLength = _data.Length;
+			foreach (LiveKeyframe kf in data.Keyframes)
 			{
 				_oldTimes[kf] = kf.Time;
 			}
@@ -38,15 +38,15 @@ namespace SPNATI_Character_Editor.Actions
 		{
 			Time = (float)Math.Round(Time, 3);
 			//delete any keyframes that are moving out of range, or adjust their positions so the absolute time remains the same
-			if (Time == _sprite.Start)
+			if (Time == _data.Start)
 			{
 				return;
 			}
-			float relTime = (float)Math.Round(Time - _sprite.Start, 3);
+			float relTime = (float)Math.Round(Time - _data.Start, 3);
 			List<LiveKeyframe> frames = new List<LiveKeyframe>();
-			for (int i = 1; i < _sprite.Keyframes.Count; i++)
+			for (int i = 1; i < _data.Keyframes.Count; i++)
 			{
-				LiveKeyframe kf = _sprite.Keyframes[i];
+				LiveKeyframe kf = _data.Keyframes[i];
 				if (kf.Time <= relTime)
 				{
 					frames.Add(kf);
@@ -58,15 +58,15 @@ namespace SPNATI_Character_Editor.Actions
 			}
 			foreach (LiveKeyframe kf in frames)
 			{
-				DeleteKeyframeCommand deletion = new DeleteKeyframeCommand(_sprite, kf);
+				DeleteKeyframeCommand deletion = new DeleteKeyframeCommand(_data, kf);
 				deletion.Do();
 				_deletions[kf] = deletion;
 			}
 
 			//move the start point
-			float duration = _sprite.Start + _sprite.Length - Time;
-			_sprite.Start = Time;
-			_sprite.Length = duration;
+			float duration = _data.Start + _data.Length - Time;
+			_data.Start = Time;
+			_data.Length = duration;
 
 			//add back any deletions that are no longer deleted
 			frames.Clear();
@@ -93,8 +93,8 @@ namespace SPNATI_Character_Editor.Actions
 
 		public void Undo()
 		{
-			_sprite.Start = _oldStart;
-			_sprite.Length = _oldLength;
+			_data.Start = _oldStart;
+			_data.Length = _oldLength;
 			foreach (KeyValuePair<LiveKeyframe, float> kvp in _oldTimes)
 			{
 				LiveKeyframe kf = kvp.Key;
diff --git a/editor source/SPNATI Character Editor/Actions/PasteKeyframeCommand.cs b/editor source/SPNATI Character Editor/Actions/PasteKeyframeCommand.cs
index c1b10f116fbeba267738fd062a98a06cc63efed2..8edf9241d0df927d160b55d8c99afebff8c1a3bb 100644
--- a/editor source/SPNATI Character Editor/Actions/PasteKeyframeCommand.cs	
+++ b/editor source/SPNATI Character Editor/Actions/PasteKeyframeCommand.cs	
@@ -6,39 +6,39 @@ namespace SPNATI_Character_Editor.Actions
 {
 	public class PasteKeyframeCommand : ICommand
 	{
-		private LiveSprite _sprite;
+		private LiveAnimatedObject _object;
 		private LiveKeyframe _keyframe;
 		private float _time;
 		public LiveKeyframe NewKeyframe;
 		private LiveKeyframe _original;
 
-		public PasteKeyframeCommand(LiveSprite sprite, LiveKeyframe keyframe, float time)
+		public PasteKeyframeCommand(LiveAnimatedObject obj, LiveKeyframe keyframe, float time)
 		{
-			_sprite = sprite;
+			_object = obj;
 			_keyframe = keyframe;
-			_time = time - sprite.Start;
-			_original = sprite.Keyframes.Find(f => f.Time == time);
+			_time = time - obj.Start;
+			_original = obj.Keyframes.Find(f => f.Time == time);
 			if (_original != null)
 			{
-				_original = sprite.CopyKeyframe(_original, new HashSet<string>());
+				_original = obj.CopyKeyframe(_original, new HashSet<string>());
 			}
 		}
 
 		public void Do()
 		{
-			NewKeyframe = _sprite.PasteKeyframe(_keyframe, _time, NewKeyframe);
+			NewKeyframe = _object.PasteKeyframe(_keyframe, _time, NewKeyframe);
 		}
 
 		public void Undo()
 		{
 			if (_original == null)
 			{
-				_sprite.RemoveKeyframe(NewKeyframe);
+				_object.RemoveKeyframe(NewKeyframe);
 			}
 			else
 			{
 				NewKeyframe.Clear();
-				_sprite.PasteKeyframe(_original, _time, NewKeyframe);
+				_object.PasteKeyframe(_original, _time, NewKeyframe);
 			}
 		}
 	}
diff --git a/editor source/SPNATI Character Editor/Actions/TimelineActions/ModifyWidgetLengthTimelineAction.cs b/editor source/SPNATI Character Editor/Actions/TimelineActions/ModifyWidgetLengthTimelineAction.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c69af7466bc7618f287708fb4909eea268c4683f
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Actions/TimelineActions/ModifyWidgetLengthTimelineAction.cs	
@@ -0,0 +1,89 @@
+using System;
+using System.Windows.Forms;
+using Desktop;
+using SPNATI_Character_Editor.EpilogueEditor;
+
+namespace SPNATI_Character_Editor.Actions.TimelineActions
+{
+	/// <summary>
+	/// Resizing the end point of a widget that has no keyframes
+	/// </summary>
+	public class ModifyWidgetLengthTimelineAction : ITimelineAction, ICommand
+	{
+		private KeyframedWidget _widget;
+		private LiveAnimatedObject _object;
+
+		private float _oldLength;
+		private bool _moved;
+
+		public float Length;
+		private ITimelineData _data;
+		private UndoManager _history;
+
+		public Cursor GetHoverCursor()
+		{
+			return Cursors.SizeWE;
+		}
+
+		public Cursor GetCursor()
+		{
+			return Cursors.SizeWE;
+		}
+
+		public void Start(WidgetActionArgs args)
+		{
+			_history = args.History;
+			_data = args.Data;
+			_widget = args.Widget as KeyframedWidget;
+			if (_widget == null)
+			{
+				throw new NotSupportedException();
+			}
+			_object = _widget.Data;
+			_oldLength = _object.Start + _object.Length;
+			_widget.OnStartMove(args);
+		}
+
+		public void Update(WidgetActionArgs args)
+		{
+			bool snap = !args.Modifiers.HasFlag(Keys.Shift);
+			float time = !snap ? args.Time : args.SnapTime();
+			float length = Math.Max(snap ? args.SnapIncrement : 0.01f, time - _object.Start);
+			if (length != _object.Length)
+			{
+				if (_moved)
+				{
+					Undo();
+				}
+				_moved = true;
+				Length = length;
+				Do();
+			}
+		}
+
+		public void Finish()
+		{
+			if (_moved)
+			{
+				Undo();
+				_history?.Commit(this);
+			}
+		}
+
+		public void Do()
+		{
+			if (_moved)
+			{
+				_object.Length = Length;
+			}
+		}
+
+		public void Undo()
+		{
+			if (_moved)
+			{
+				_object.Length = _oldLength;
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Actions/TimelineActions/MoveWidgetTimelineAction.cs b/editor source/SPNATI Character Editor/Actions/TimelineActions/MoveWidgetTimelineAction.cs
index 95f0a34466c5450a6b66e83f3dfefc6e095b27a4..689163be7ff6025a60a9fd0b6dd8779cb0bf5552 100644
--- a/editor source/SPNATI Character Editor/Actions/TimelineActions/MoveWidgetTimelineAction.cs	
+++ b/editor source/SPNATI Character Editor/Actions/TimelineActions/MoveWidgetTimelineAction.cs	
@@ -10,9 +10,8 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 	/// </summary>
 	public class MoveWidgetTimelineAction : ITimelineAction, ICommand
 	{
-		private ITimelineWidget _widget;
+		private ITimelineObject _widget;
 
-		public float Length;
 		private bool _moved;
 		private float _oldStart;
 		private float _startOffset;
@@ -23,6 +22,13 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 		private ITimelineData _data;
 		private UndoManager _history;
 
+		private bool _allowTrackSwitch;
+
+		public MoveWidgetTimelineAction(bool allowTrackSwitch)
+		{
+			_allowTrackSwitch = allowTrackSwitch;
+		}
+
 		public Cursor GetHoverCursor()
 		{
 			return Cursors.Default;
@@ -48,15 +54,19 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 		public void Update(WidgetActionArgs args)
 		{
 			int track = args.Track;
-			float start = Math.Max(0, args.SnapTime() - _startOffset);
-			if (start != _widget.GetStart() || track != Track)
+			float time = args.Modifiers.HasFlag(Keys.Shift) ? args.Time : args.SnapTime();
+			float start = Math.Max(0, time - _startOffset);
+			if (start != _widget.GetStart() || (_allowTrackSwitch && track != Track))
 			{
 				if (_moved)
 				{
 					Undo();
 				}
 				_moved = true;
-				Track = track;
+				if (_allowTrackSwitch)
+				{
+					Track = track;
+				}
 				Time = start;
 				Do();
 			}
diff --git a/editor source/SPNATI Character Editor/Actions/TimelineActions/SelectKeyframeTimelineAction.cs b/editor source/SPNATI Character Editor/Actions/TimelineActions/SelectKeyframeTimelineAction.cs
index 5913ac19245403d561910f0b4b902215b197a8bf..8179d88bad47ccdb8198c994752ae3e341ac6f4c 100644
--- a/editor source/SPNATI Character Editor/Actions/TimelineActions/SelectKeyframeTimelineAction.cs	
+++ b/editor source/SPNATI Character Editor/Actions/TimelineActions/SelectKeyframeTimelineAction.cs	
@@ -9,17 +9,19 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 	public class SelectKeyframeTimelineAction : ITimelineAction
 	{
 		private Timeline _timeline;
-		private SpriteWidget _widget;
+		private KeyframedWidget _widget;
 		private ITimeCommand _command;
 		private LiveKeyframe _keyframe;
 		private string _property;
-		private LiveSprite _sprite;
+		private LiveAnimatedObject _data;
 		private bool _movable;
 		private bool _movingStart;
 		private float _startTime;
 		private bool _dragging;
+		private bool _copyOnMove;
+		private LiveKeyframe _originalFrame;
 
-		public SelectKeyframeTimelineAction(SpriteWidget widget, LiveKeyframe keyframe, string property)
+		public SelectKeyframeTimelineAction(KeyframedWidget widget, LiveKeyframe keyframe, string property)
 		{
 			_widget = widget;
 			_movable = (_widget.SelectedProperties.Count == 0 && property == null) || keyframe.Time != 0;
@@ -40,12 +42,13 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 		public void Start(WidgetActionArgs args)
 		{
 			_timeline = args.Timeline;
-			_sprite = args.Widget.GetData() as LiveSprite;
+			_data = args.Widget.GetData() as LiveAnimatedObject;
+			_copyOnMove = args.Modifiers.HasFlag(Keys.Control) && _data.Keyframes.IndexOf(_keyframe) == _data.Keyframes.Count - 1;
 			_widget.SelectKeyframe(_keyframe, _property, args.Modifiers.HasFlag(Keys.Control));
-			_timeline.CurrentTime = _sprite.Start + _keyframe.Time;
+			_timeline.CurrentTime = _data.Start + _keyframe.Time;
 			_startTime = args.Time;
 
-			LiveKeyframe interpolation = _sprite.GetInterpolatedFrame(_keyframe.Time);
+			LiveKeyframe interpolation = _data.GetInterpolatedFrame(_keyframe.Time);
 			_timeline.SelectData(_keyframe, interpolation);
 		}
 
@@ -60,7 +63,7 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 			float snappedTime = snap ? args.SnapTime() : args.Time;
 			if (_widget.SelectedProperties.Count > 0 || _keyframe.Time > 0)
 			{
-				snappedTime -= _sprite.Start;
+				snappedTime -= _data.Start;
 			}
 			if (snap)
 			{
@@ -72,7 +75,12 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 			}
 			snappedTime = (float)Math.Round(snappedTime, 2);
 
-			LiveKeyframe frameAtTime = _sprite.Keyframes.Find(k => k.Time == snappedTime);
+			if (_originalFrame != null && snappedTime <= _originalFrame.Time)
+			{
+				return;
+			}
+
+			LiveKeyframe frameAtTime = _data.Keyframes.Find(k => k.Time == snappedTime);
 			if (_movingStart || frameAtTime == null || (_property != null && !frameAtTime.HasProperty(_property)))
 			{
 				if (_movingStart || _keyframe.Time != snappedTime)
@@ -81,12 +89,24 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 					{
 						if (_widget.SelectedProperties.Count == 0 && _keyframe.Time == 0)
 						{
-							_command = new MoveSpriteStartCommand(_sprite, _sprite.Start);
+							_command = new MoveSpriteStartCommand(_data, _data.Start);
 							_movingStart = true;
 						}
 						else
 						{
-							_command = new MoveKeyframeCommand(_sprite, _keyframe, _widget.SelectedProperties);
+							if (_copyOnMove && snappedTime > _keyframe.Time)
+							{
+								HashSet<string> props = new HashSet<string>();
+								foreach (string prop in _widget.SelectedProperties)
+								{
+									props.Add(prop);
+								}
+								_originalFrame = _keyframe;
+								LiveKeyframe copy = _data.CopyKeyframe(_keyframe, props);
+								_data.AddKeyframe(copy);
+								_keyframe = copy;
+							}
+							_command = new MoveKeyframeCommand(_data, _keyframe, _widget.SelectedProperties);
 							_command.Do();
 						}
 					}
@@ -125,7 +145,7 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 
 	public class MoveKeyframeCommand : ITimeCommand
 	{
-		private LiveSprite _sprite;
+		private LiveAnimatedObject _data;
 		private LiveKeyframe _keyframe;
 		private float _oldTime;
 		private bool _moveFrame;
@@ -135,10 +155,10 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 
 		public float Time;
 
-		public MoveKeyframeCommand(LiveSprite sprite, LiveKeyframe keyframe, List<string> properties)
+		public MoveKeyframeCommand(LiveAnimatedObject data, LiveKeyframe keyframe, List<string> properties)
 		{
 			Time = keyframe.Time;
-			_sprite = sprite;
+			_data = data;
 			_keyframe = keyframe;
 			_oldTime = _keyframe.Time;
 			_moveFrame = properties.Count == 0;
@@ -154,7 +174,7 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 			}
 			else
 			{
-				_newFrame = _sprite.MoveProperty(_keyframe, _properties, Time, NewKeyframe);
+				_newFrame = _data.MoveProperty(_keyframe, _properties, Time, NewKeyframe);
 			}
 		}
 
@@ -164,7 +184,7 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 		/// <param name="time"></param>
 		public void Update(float time)
 		{
-			Time = time - _sprite.Start;
+			Time = time - _data.Start;
 			Time = (float)Math.Round(time, 2);
 			if (_moveFrame)
 			{
@@ -172,7 +192,7 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 			}
 			else
 			{
-				_newFrame = _sprite.MoveProperty(NewKeyframe, _properties, Time, null);
+				_newFrame = _data.MoveProperty(NewKeyframe, _properties, Time, null);
 			}
 		}
 
@@ -185,7 +205,7 @@ namespace SPNATI_Character_Editor.Actions.TimelineActions
 			else
 			{
 				_keyframe.Time = _oldTime;
-				_sprite.MoveProperty(NewKeyframe, _properties, _oldTime, _keyframe);
+				_data.MoveProperty(NewKeyframe, _properties, _oldTime, _keyframe);
 			}
 		}
 	}
diff --git a/editor source/SPNATI Character Editor/Actions/TimelineActions/WidgetEndTimelineAction.cs b/editor source/SPNATI Character Editor/Actions/TimelineActions/WidgetEndTimelineAction.cs
deleted file mode 100644
index 0460faf38bef6169020429e290cd1a66cc7e54e5..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Actions/TimelineActions/WidgetEndTimelineAction.cs	
+++ /dev/null
@@ -1,80 +0,0 @@
-using System;
-using System.Windows.Forms;
-using Desktop;
-using SPNATI_Character_Editor.EpilogueEditor;
-
-namespace SPNATI_Character_Editor.Actions.TimelineActions
-{
-	/// <summary>
-	/// Dragging the end position of a widget
-	/// </summary>
-	public class WidgetEndTimelineAction : ITimelineAction, ICommand
-	{
-		private ITimelineWidget _widget;
-		private UndoManager _history;
-		private float _endMin;
-		private float _endMax;
-		private int _snap;
-		private float _oldLength;
-
-		public float Length;
-
-		public float End;
-
-
-		public Cursor GetHoverCursor()
-		{
-			return GetCursor();
-		}
-
-		public Cursor GetCursor()
-		{
-			return Cursors.SizeWE;
-		}
-
-		public void Start(WidgetActionArgs args)
-		{
-			_history = args.History;
-			_widget = args.Widget;
-			_snap = (int)Math.Round(1 / args.SnapIncrement);
-			_endMax = float.MaxValue;
-			_endMin = _widget.GetStart() + args.SnapIncrement;
-			_oldLength = _widget.GetLength(args.Duration);
-			Length = _oldLength;
-			Do();
-		}
-
-		public void Update(WidgetActionArgs args)
-		{
-			float time = args.Time;
-			if (!args.Modifiers.HasFlag(Keys.Shift))
-			{
-				time = (float)Math.Round((float)Math.Round(time * _snap) / _snap, 2);
-			}
-			time = Math.Max(_endMin, Math.Min(time, _endMax));
-			float length = time - _widget.GetStart();
-			if (length != Length)
-			{
-				Undo();
-				Length = length;
-				Do();
-			}
-		}
-
-		public void Finish()
-		{
-			Undo();
-			_history?.Commit(this);
-		}
-
-		public void Do()
-		{
-			_widget.SetLength(Length);
-		}
-
-		public void Undo()
-		{
-			_widget.SetLength(_oldLength);
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Actions/TimelineActions/WidgetStartTimelineAction.cs b/editor source/SPNATI Character Editor/Actions/TimelineActions/WidgetStartTimelineAction.cs
deleted file mode 100644
index 984b05f8f34fbf5426e24c5d916bfba7b2960a71..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Actions/TimelineActions/WidgetStartTimelineAction.cs	
+++ /dev/null
@@ -1,80 +0,0 @@
-using System;
-using System.Windows.Forms;
-using Desktop;
-using SPNATI_Character_Editor.EpilogueEditor;
-
-namespace SPNATI_Character_Editor.Actions.TimelineActions
-{
-	/// <summary>
-	/// Dragging the start position of a widget
-	/// </summary>
-	public class WidgetStartTimelineAction : ITimelineAction, ICommand
-	{
-		private ITimelineWidget _widget;
-		private float _startMax;
-		private float _startMin;
-		private int _snap;
-		private float _endTime;
-		private float _oldDelay;
-		private float _oldLength;
-		private UndoManager _history;
-
-		public float Delay;
-
-		public Cursor GetHoverCursor()
-		{
-			return GetCursor();
-		}
-
-		public Cursor GetCursor()
-		{
-			return Cursors.SizeWE;
-		}
-
-		public void Start(WidgetActionArgs args)
-		{
-			_history = args.History;
-			_widget = args.Widget;
-			_snap = (int)Math.Round(1 / args.SnapIncrement);
-			_endTime = _widget.GetEnd(args.Duration);
-			_startMin = 0;
-			_startMax = _endTime - args.SnapIncrement;
-			_oldDelay = _widget.GetStart();
-			_oldLength = _widget.GetLength(args.Duration);
-			Delay = _oldDelay;
-			Do();
-		}
-
-		public void Update(WidgetActionArgs args)
-		{
-			float time = args.Time;
-			time = (float)Math.Round(Math.Round(time * _snap) / _snap, 2);
-			float delay = Math.Max(_startMin, Math.Min(time, _startMax));
-			if (delay != Delay)
-			{
-				Undo();
-				Delay = delay;
-				Do();
-			}
-		}
-
-		public void Finish()
-		{
-			Undo();
-			_history.Commit(this);
-		}
-
-		public void Do()
-		{
-			float start = Math.Min(_endTime, Delay);
-			_widget.SetStart(start);
-			_widget.SetLength(Math.Max(0, _endTime - start));
-		}
-
-		public void Undo()
-		{
-			_widget.SetStart(_oldDelay);
-			_widget.SetLength(_oldLength);
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Actions/ToggleAnimationBreakCommand.cs b/editor source/SPNATI Character Editor/Actions/ToggleAnimationBreakCommand.cs
deleted file mode 100644
index 09c156b430b8a502666ecdd837e65d53a16af0cf..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Actions/ToggleAnimationBreakCommand.cs	
+++ /dev/null
@@ -1,87 +0,0 @@
-using Desktop;
-using SPNATI_Character_Editor.EpilogueEditor;
-using System.Collections.Generic;
-using System;
-
-namespace SPNATI_Character_Editor.Actions
-{
-	/// <summary>
-	/// Toggles one or more properties in a keyframe to use an anim break
-	/// </summary>
-	public class ToggleAnimationBreakCommand : ICommand
-	{
-		private HashSet<string> _properties = new HashSet<string>();
-		private Dictionary<string, bool> _oldSettings = new Dictionary<string, bool>();
-		private LiveSprite _sprite;
-		private LiveKeyframe _keyframe;
-		private bool _enable;
-
-		public ToggleAnimationBreakCommand(LiveSprite sprite, LiveKeyframe frame, HashSet<string> properties)
-		{
-			_sprite = sprite;
-			_keyframe = frame;
-			if (properties.Count == 0)
-			{
-				foreach (string property in LiveKeyframe.TrackedProperties)
-				{
-					if (frame.HasProperty(property))
-					{
-						properties.Add(property);
-					}
-				}
-			}
-			foreach (string prop in properties)
-			{
-				_properties.Add(prop);
-			}
-
-			bool anyOff = false; //turn on for everything unless all selected properties were already on
-			foreach (string property in _properties)
-			{
-				bool isBreak = _keyframe.InterpolationBreaks.ContainsKey(property);
-				_oldSettings[property] = isBreak;
-				anyOff = anyOff || !isBreak;
-			}
-			if (anyOff)
-			{
-				_enable = true;
-			}
-			_properties = properties;
-		}
-
-		public void Do()
-		{
-			_oldSettings.Clear();
-			foreach (string property in _properties)
-			{
-				_oldSettings[property] = _keyframe.InterpolationBreaks.ContainsKey(property);
-				if (_enable)
-				{
-					_keyframe.InterpolationBreaks[property] = true;
-				}
-				else
-				{
-					_keyframe.InterpolationBreaks.Remove(property);
-					AnimatedProperty prop = _sprite.GetAnimationProperties(property);
-					prop.Ease.RemoveValue(_keyframe.Time);
-					prop.Interpolation.RemoveValue(_keyframe.Time);
-				}
-			}
-		}
-
-		public void Undo()
-		{
-			foreach (KeyValuePair<string, bool> kvp in _oldSettings)
-			{
-				if (kvp.Value)
-				{
-					_keyframe.InterpolationBreaks[kvp.Key] = true;
-				}
-				else
-				{
-					_keyframe.InterpolationBreaks.Remove(kvp.Key);
-				}
-			}
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Actions/ToggleKeyframeTypeCommand.cs b/editor source/SPNATI Character Editor/Actions/ToggleKeyframeTypeCommand.cs
new file mode 100644
index 0000000000000000000000000000000000000000..952893910cbe530e1b4f2739d45809c63107bc30
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Actions/ToggleKeyframeTypeCommand.cs	
@@ -0,0 +1,79 @@
+using System.Collections.Generic;
+using Desktop;
+using SPNATI_Character_Editor.EpilogueEditor;
+
+namespace SPNATI_Character_Editor.Actions
+{
+	/// <summary>
+	/// Toggles one or more properties in a keyframe to use an anim break
+	/// </summary>
+	public class ToggleKeyframeTypeCommand : ICommand
+	{
+		private HashSet<string> _properties = new HashSet<string>();
+		private Dictionary<string, KeyframeType> _oldSettings = new Dictionary<string, KeyframeType>();
+		private Dictionary<string, KeyframeType> _newSettings = new Dictionary<string, KeyframeType>();
+		private LiveAnimatedObject _data;
+		private LiveKeyframe _keyframe;
+
+		public ToggleKeyframeTypeCommand(LiveAnimatedObject data, LiveKeyframe frame, HashSet<string> properties)
+		{
+			_data = data;
+			_keyframe = frame;
+			if (properties.Count == 0)
+			{
+				foreach (string property in frame.TrackedProperties)
+				{
+					if (frame.HasProperty(property))
+					{
+						properties.Add(property);
+					}
+				}
+			}
+			foreach (string prop in properties)
+			{
+				_properties.Add(prop);
+			}
+
+			foreach (string property in _properties)
+			{
+				KeyframeType type = _keyframe.GetFrameType(property);
+				_oldSettings[property] = type;
+				switch (type)
+				{
+					case KeyframeType.Begin:
+						type = KeyframeType.Split;
+						break;
+					case KeyframeType.Split:
+						type = KeyframeType.Normal;
+						break;
+					default:
+						type = KeyframeType.Begin;
+						break;
+				}
+				_newSettings[property] = type;
+			}
+			_properties = properties;
+		}
+
+		public void Do()
+		{
+			_oldSettings.Clear();
+			foreach (string property in _properties)
+			{
+				KeyframeType type = _newSettings[property];
+				LiveKeyframeMetadata metadata = _keyframe.GetMetadata(property, true);
+				metadata.FrameType = type;
+			}
+		}
+
+		public void Undo()
+		{
+			foreach (KeyValuePair<string, KeyframeType> kvp in _oldSettings)
+			{
+				KeyframeType type = kvp.Value;
+				LiveKeyframeMetadata metadata = _keyframe.GetMetadata(kvp.Key, true);
+				metadata.FrameType = type;
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.Designer.cs
index 01f9e691f086cb6e6b875ec9d9f870e5840cd4cf..4221d9cdbd8296b1128ade51fe2718081e928437 100644
--- a/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.Designer.cs	
@@ -31,25 +31,56 @@
 			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
 			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
 			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
-			this.valScale = new System.Windows.Forms.NumericUpDown();
-			this.lblScale = new System.Windows.Forms.Label();
-			this.gridLabels = new System.Windows.Forms.DataGridView();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle5 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle6 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle8 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle9 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.valScale = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.lblScale = new Desktop.Skinning.SkinnedLabel();
+			this.gridLabels = new Desktop.Skinning.SkinnedDataGridView();
 			this.colLabelsStage = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.colLabelsLabel = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.lblStageLabels = new System.Windows.Forms.Label();
+			this.lblStageLabels = new Desktop.Skinning.SkinnedLabel();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.cboDialogueLayer = new Desktop.Skinning.SkinnedComboBox();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.valLayer = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.skinnedGroupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.skinnedGroupBox2 = new Desktop.Skinning.SkinnedGroupBox();
+			this.gridOtherNicknames = new Desktop.Skinning.SkinnedDataGridView();
+			this.ColOtherNickname = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.gridNicknames = new Desktop.Skinning.SkinnedDataGridView();
+			this.ColCharacter = new Desktop.CommonControls.RecordColumn();
+			this.ColNickname = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.label4 = new System.Windows.Forms.Label();
+			this.label3 = new System.Windows.Forms.Label();
+			this.skinnedGroupBox3 = new Desktop.Skinning.SkinnedGroupBox();
+			this.styleControl = new SPNATI_Character_Editor.Controls.StyleControl();
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
 			((System.ComponentModel.ISupportInitialize)(this.valScale)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridLabels)).BeginInit();
+			((System.ComponentModel.ISupportInitialize)(this.valLayer)).BeginInit();
+			this.skinnedGroupBox1.SuspendLayout();
+			this.skinnedGroupBox2.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.gridOtherNicknames)).BeginInit();
+			((System.ComponentModel.ISupportInitialize)(this.gridNicknames)).BeginInit();
+			this.skinnedGroupBox3.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// valScale
 			// 
+			this.valScale.BackColor = System.Drawing.Color.White;
 			this.valScale.DecimalPlaces = 1;
+			this.valScale.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valScale.ForeColor = System.Drawing.Color.Black;
 			this.valScale.Increment = new decimal(new int[] {
             1,
             0,
             0,
             65536});
-			this.valScale.Location = new System.Drawing.Point(115, 121);
+			this.valScale.Location = new System.Drawing.Point(133, 141);
 			this.valScale.Maximum = new decimal(new int[] {
             150,
             0,
@@ -72,7 +103,11 @@
 			// lblScale
 			// 
 			this.lblScale.AutoSize = true;
-			this.lblScale.Location = new System.Drawing.Point(4, 124);
+			this.lblScale.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblScale.ForeColor = System.Drawing.Color.Black;
+			this.lblScale.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblScale.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblScale.Location = new System.Drawing.Point(6, 144);
 			this.lblScale.Name = "lblScale";
 			this.lblScale.Size = new System.Drawing.Size(84, 13);
 			this.lblScale.TabIndex = 88;
@@ -82,6 +117,9 @@
 			// 
 			this.gridLabels.AllowUserToResizeColumns = false;
 			this.gridLabels.AllowUserToResizeRows = false;
+			this.gridLabels.BackgroundColor = System.Drawing.Color.White;
+			this.gridLabels.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridLabels.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
 			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
 			dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control;
 			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
@@ -103,8 +141,12 @@
 			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
 			this.gridLabels.DefaultCellStyle = dataGridViewCellStyle2;
 			this.gridLabels.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
-			this.gridLabels.Location = new System.Drawing.Point(115, 0);
+			this.gridLabels.EnableHeadersVisualStyles = false;
+			this.gridLabels.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridLabels.GridColor = System.Drawing.Color.LightGray;
+			this.gridLabels.Location = new System.Drawing.Point(133, 24);
 			this.gridLabels.Name = "gridLabels";
+			this.gridLabels.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
 			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
 			dataGridViewCellStyle3.BackColor = System.Drawing.SystemColors.Control;
 			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
@@ -134,36 +176,317 @@
 			// lblStageLabels
 			// 
 			this.lblStageLabels.AutoSize = true;
-			this.lblStageLabels.Location = new System.Drawing.Point(4, 4);
+			this.lblStageLabels.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblStageLabels.ForeColor = System.Drawing.Color.Black;
+			this.lblStageLabels.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblStageLabels.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblStageLabels.Location = new System.Drawing.Point(6, 28);
 			this.lblStageLabels.Name = "lblStageLabels";
 			this.lblStageLabels.Size = new System.Drawing.Size(99, 13);
 			this.lblStageLabels.TabIndex = 86;
 			this.lblStageLabels.Text = "Vary label by stage:";
+			// 
+			// label1
+			// 
+			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.Black;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(6, 170);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(121, 13);
+			this.label1.TabIndex = 90;
+			this.label1.Text = "Speech bubble position:";
+			// 
+			// cboDialogueLayer
+			// 
+			this.cboDialogueLayer.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboDialogueLayer.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboDialogueLayer.BackColor = System.Drawing.Color.White;
+			this.cboDialogueLayer.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboDialogueLayer.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboDialogueLayer.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboDialogueLayer.FormattingEnabled = true;
+			this.cboDialogueLayer.Location = new System.Drawing.Point(133, 167);
+			this.cboDialogueLayer.Name = "cboDialogueLayer";
+			this.cboDialogueLayer.SelectedIndex = -1;
+			this.cboDialogueLayer.SelectedItem = null;
+			this.cboDialogueLayer.Size = new System.Drawing.Size(121, 21);
+			this.cboDialogueLayer.Sorted = false;
+			this.cboDialogueLayer.TabIndex = 91;
+			// 
+			// label2
+			// 
+			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.Black;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label2.Location = new System.Drawing.Point(6, 196);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(42, 13);
+			this.label2.TabIndex = 92;
+			this.label2.Text = "Z layer:";
+			// 
+			// valLayer
+			// 
+			this.valLayer.BackColor = System.Drawing.Color.White;
+			this.valLayer.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valLayer.ForeColor = System.Drawing.Color.Black;
+			this.valLayer.Location = new System.Drawing.Point(133, 194);
+			this.valLayer.Name = "valLayer";
+			this.valLayer.Size = new System.Drawing.Size(66, 20);
+			this.valLayer.TabIndex = 93;
+			// 
+			// skinnedGroupBox1
+			// 
+			this.skinnedGroupBox1.Controls.Add(this.gridLabels);
+			this.skinnedGroupBox1.Controls.Add(this.valLayer);
+			this.skinnedGroupBox1.Controls.Add(this.lblStageLabels);
+			this.skinnedGroupBox1.Controls.Add(this.label2);
+			this.skinnedGroupBox1.Controls.Add(this.valScale);
+			this.skinnedGroupBox1.Controls.Add(this.cboDialogueLayer);
+			this.skinnedGroupBox1.Controls.Add(this.lblScale);
+			this.skinnedGroupBox1.Controls.Add(this.label1);
+			this.skinnedGroupBox1.Location = new System.Drawing.Point(6, 6);
+			this.skinnedGroupBox1.Name = "skinnedGroupBox1";
+			this.skinnedGroupBox1.Size = new System.Drawing.Size(420, 220);
+			this.skinnedGroupBox1.TabIndex = 94;
+			this.skinnedGroupBox1.TabStop = false;
+			this.skinnedGroupBox1.Text = "Advanced Metadata";
+			// 
+			// skinnedGroupBox2
+			// 
+			this.skinnedGroupBox2.Controls.Add(this.gridOtherNicknames);
+			this.skinnedGroupBox2.Controls.Add(this.gridNicknames);
+			this.skinnedGroupBox2.Controls.Add(this.label4);
+			this.skinnedGroupBox2.Controls.Add(this.label3);
+			this.skinnedGroupBox2.Location = new System.Drawing.Point(6, 232);
+			this.skinnedGroupBox2.Name = "skinnedGroupBox2";
+			this.skinnedGroupBox2.Size = new System.Drawing.Size(420, 284);
+			this.skinnedGroupBox2.TabIndex = 95;
+			this.skinnedGroupBox2.TabStop = false;
+			this.skinnedGroupBox2.Text = "Nicknames";
+			// 
+			// gridOtherNicknames
+			// 
+			this.gridOtherNicknames.AllowUserToDeleteRows = false;
+			this.gridOtherNicknames.AllowUserToResizeColumns = false;
+			this.gridOtherNicknames.AllowUserToResizeRows = false;
+			this.gridOtherNicknames.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridOtherNicknames.BackgroundColor = System.Drawing.Color.White;
+			this.gridOtherNicknames.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridOtherNicknames.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle4.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle4.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle4.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle4.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle4.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle4.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridOtherNicknames.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle4;
+			this.gridOtherNicknames.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+			this.gridOtherNicknames.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.ColOtherNickname});
+			dataGridViewCellStyle5.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle5.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle5.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle5.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle5.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle5.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridOtherNicknames.DefaultCellStyle = dataGridViewCellStyle5;
+			this.gridOtherNicknames.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
+			this.gridOtherNicknames.EnableHeadersVisualStyles = false;
+			this.gridOtherNicknames.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridOtherNicknames.GridColor = System.Drawing.Color.LightGray;
+			this.gridOtherNicknames.Location = new System.Drawing.Point(246, 43);
+			this.gridOtherNicknames.Name = "gridOtherNicknames";
+			this.gridOtherNicknames.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle6.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle6.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle6.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle6.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle6.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle6.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridOtherNicknames.RowHeadersDefaultCellStyle = dataGridViewCellStyle6;
+			this.gridOtherNicknames.RowHeadersVisible = false;
+			this.gridOtherNicknames.Size = new System.Drawing.Size(165, 235);
+			this.gridOtherNicknames.TabIndex = 3;
+			// 
+			// ColOtherNickname
+			// 
+			this.ColOtherNickname.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.ColOtherNickname.HeaderText = "Nickname";
+			this.ColOtherNickname.Name = "ColOtherNickname";
+			// 
+			// gridNicknames
+			// 
+			this.gridNicknames.AllowUserToDeleteRows = false;
+			this.gridNicknames.AllowUserToResizeColumns = false;
+			this.gridNicknames.AllowUserToResizeRows = false;
+			this.gridNicknames.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+			this.gridNicknames.BackgroundColor = System.Drawing.Color.White;
+			this.gridNicknames.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridNicknames.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle7.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle7.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle7.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle7.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle7.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle7.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridNicknames.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle7;
+			this.gridNicknames.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+			this.gridNicknames.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.ColCharacter,
+            this.ColNickname});
+			dataGridViewCellStyle8.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle8.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle8.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle8.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle8.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle8.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridNicknames.DefaultCellStyle = dataGridViewCellStyle8;
+			this.gridNicknames.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
+			this.gridNicknames.EnableHeadersVisualStyles = false;
+			this.gridNicknames.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridNicknames.GridColor = System.Drawing.Color.LightGray;
+			this.gridNicknames.Location = new System.Drawing.Point(9, 43);
+			this.gridNicknames.Name = "gridNicknames";
+			this.gridNicknames.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle9.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle9.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle9.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle9.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle9.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle9.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridNicknames.RowHeadersDefaultCellStyle = dataGridViewCellStyle9;
+			this.gridNicknames.RowHeadersVisible = false;
+			this.gridNicknames.Size = new System.Drawing.Size(231, 235);
+			this.gridNicknames.TabIndex = 2;
+			// 
+			// ColCharacter
+			// 
+			this.ColCharacter.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.ColCharacter.HeaderText = "Character";
+			this.ColCharacter.Name = "ColCharacter";
+			this.ColCharacter.RecordType = null;
+			// 
+			// ColNickname
+			// 
+			this.ColNickname.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.ColNickname.HeaderText = "Nickname";
+			this.ColNickname.Name = "ColNickname";
+			// 
+			// label4
+			// 
+			this.label4.AutoSize = true;
+			this.label4.Location = new System.Drawing.Point(243, 27);
+			this.label4.Name = "label4";
+			this.label4.Size = new System.Drawing.Size(147, 13);
+			this.label4.TabIndex = 1;
+			this.label4.Text = "Nicknames for everyone else:";
+			// 
+			// label3
+			// 
+			this.label3.AutoSize = true;
+			this.label3.Location = new System.Drawing.Point(6, 27);
+			this.label3.Name = "label3";
+			this.label3.Size = new System.Drawing.Size(170, 13);
+			this.label3.TabIndex = 0;
+			this.label3.Text = "Nicknames for specific characters:";
+			// 
+			// skinnedGroupBox3
+			// 
+			this.skinnedGroupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedGroupBox3.Controls.Add(this.styleControl);
+			this.skinnedGroupBox3.Location = new System.Drawing.Point(432, 6);
+			this.skinnedGroupBox3.Name = "skinnedGroupBox3";
+			this.skinnedGroupBox3.Size = new System.Drawing.Size(544, 616);
+			this.skinnedGroupBox3.TabIndex = 96;
+			this.skinnedGroupBox3.TabStop = false;
+			this.skinnedGroupBox3.Text = "Custom Text Formatting";
+			// 
+			// styleControl
+			// 
+			this.styleControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.styleControl.Location = new System.Drawing.Point(6, 24);
+			this.styleControl.Name = "styleControl";
+			this.styleControl.Size = new System.Drawing.Size(532, 586);
+			this.styleControl.TabIndex = 0;
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel1.Location = new System.Drawing.Point(3, 519);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(423, 64);
+			this.skinnedLabel1.TabIndex = 97;
+			this.skinnedLabel1.Text = "To assign a nickname mid-game, set a per-target marker named \"nickname\" on a case" +
+    " where the desired character is the target.";
 			// 
 			// AdvancedMetadataEditor
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.valScale);
-			this.Controls.Add(this.lblScale);
-			this.Controls.Add(this.gridLabels);
-			this.Controls.Add(this.lblStageLabels);
+			this.Controls.Add(this.skinnedLabel1);
+			this.Controls.Add(this.skinnedGroupBox3);
+			this.Controls.Add(this.skinnedGroupBox2);
+			this.Controls.Add(this.skinnedGroupBox1);
 			this.Name = "AdvancedMetadataEditor";
-			this.Size = new System.Drawing.Size(924, 625);
+			this.Size = new System.Drawing.Size(979, 625);
 			((System.ComponentModel.ISupportInitialize)(this.valScale)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridLabels)).EndInit();
+			((System.ComponentModel.ISupportInitialize)(this.valLayer)).EndInit();
+			this.skinnedGroupBox1.ResumeLayout(false);
+			this.skinnedGroupBox1.PerformLayout();
+			this.skinnedGroupBox2.ResumeLayout(false);
+			this.skinnedGroupBox2.PerformLayout();
+			((System.ComponentModel.ISupportInitialize)(this.gridOtherNicknames)).EndInit();
+			((System.ComponentModel.ISupportInitialize)(this.gridNicknames)).EndInit();
+			this.skinnedGroupBox3.ResumeLayout(false);
 			this.ResumeLayout(false);
-			this.PerformLayout();
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.NumericUpDown valScale;
-		private System.Windows.Forms.Label lblScale;
-		private System.Windows.Forms.DataGridView gridLabels;
+		private Desktop.Skinning.SkinnedNumericUpDown valScale;
+		private Desktop.Skinning.SkinnedLabel lblScale;
+		private Desktop.Skinning.SkinnedDataGridView gridLabels;
 		private System.Windows.Forms.DataGridViewTextBoxColumn colLabelsStage;
 		private System.Windows.Forms.DataGridViewTextBoxColumn colLabelsLabel;
-		private System.Windows.Forms.Label lblStageLabels;
+		private Desktop.Skinning.SkinnedLabel lblStageLabels;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedComboBox cboDialogueLayer;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedNumericUpDown valLayer;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox1;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox2;
+		private Desktop.Skinning.SkinnedDataGridView gridNicknames;
+		private System.Windows.Forms.Label label4;
+		private System.Windows.Forms.Label label3;
+		private Desktop.Skinning.SkinnedDataGridView gridOtherNicknames;
+		private Desktop.CommonControls.RecordColumn ColCharacter;
+		private System.Windows.Forms.DataGridViewTextBoxColumn ColNickname;
+		private System.Windows.Forms.DataGridViewTextBoxColumn ColOtherNickname;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox3;
+		private Controls.StyleControl styleControl;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.cs b/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.cs
index 33e5d38bd1e556152e3c2770ecdea918c391c48b..e6f58b1292435fb040aad4af034b3fd5c7a03f52 100644
--- a/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.cs	
+++ b/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.cs	
@@ -1,4 +1,5 @@
 using Desktop;
+using SPNATI_Character_Editor.DataStructures;
 using System;
 using System.Windows.Forms;
 
@@ -12,6 +13,9 @@ namespace SPNATI_Character_Editor.Activities
 		public AdvancedMetadataEditor()
 		{
 			InitializeComponent();
+			
+			cboDialogueLayer.Items.AddRange(Enum.GetValues(typeof(DialogueLayer)));
+			ColCharacter.RecordType = typeof(Character);
 		}
 
 		public override string Caption
@@ -30,6 +34,10 @@ namespace SPNATI_Character_Editor.Activities
 		protected override void OnFirstActivate()
 		{
 			valScale.Value = Math.Max(valScale.Minimum, Math.Min((decimal)_character.Metadata.Scale, valScale.Maximum));
+			valLayer.Value = _character.Metadata.Z;
+			cboDialogueLayer.SelectedItem = _character.Metadata.BubblePosition;
+			LoadNicknames();
+			styleControl.SetCharacter(_character);
 		}
 
 		protected override void OnActivate()
@@ -40,7 +48,11 @@ namespace SPNATI_Character_Editor.Activities
 		public override void Save()
 		{
 			_character.Metadata.Scale = (float)valScale.Value;
+			_character.Metadata.BubblePosition = (DialogueLayer)cboDialogueLayer.SelectedItem;
+			_character.Metadata.Z = (int)valLayer.Value;
 			SaveLabels();
+			SaveNicknames();
+			styleControl.Save();
 		}
 
 		/// <summary>
@@ -78,5 +90,46 @@ namespace SPNATI_Character_Editor.Activities
 				}
 			}
 		}
+
+		private void LoadNicknames()
+		{
+			gridNicknames.Rows.Clear();
+			gridOtherNicknames.Rows.Clear();
+			foreach (Nickname nickname in _character.Nicknames)
+			{
+				if (nickname.Character == "*")
+				{
+					gridOtherNicknames.Rows.Add(new object[] { nickname.Label });
+				}
+				else
+				{
+					gridNicknames.Rows.Add(new object[] { nickname.Character, nickname.Label });
+				}
+			}
+		}
+
+		private void SaveNicknames()
+		{
+			_character.Nicknames.Clear();
+			foreach (DataGridViewRow row in gridNicknames.Rows)
+			{
+				string key = row.Cells[0].Value?.ToString();
+				string value = row.Cells[1].Value?.ToString();
+				if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)) { continue; }
+
+				Nickname name = new Nickname(key, value);
+				_character.Nicknames.Add(name);
+			}
+			foreach (DataGridViewRow row in gridOtherNicknames.Rows)
+			{
+				string value = row.Cells[0].Value?.ToString();
+				if (string.IsNullOrEmpty(value)) { continue; }
+
+				Nickname name = new Nickname("*", value);
+				_character.Nicknames.Add(name);
+			}
+
+			_character.Nicknames.Sort();
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.resx b/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.resx
index 2fe12d21cc4851acd2177538d95e35c04767ff58..820e76a327f3971b248d99186af2c9c468461846 100644
--- a/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.resx	
+++ b/editor source/SPNATI Character Editor/Activities/AdvancedMetadataEditor.resx	
@@ -123,4 +123,13 @@
   <metadata name="colLabelsLabel.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
+  <metadata name="ColOtherNickname.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="ColCharacter.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="ColNickname.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
 </root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Activities/BanterWizard.Designer.cs b/editor source/SPNATI Character Editor/Activities/BanterWizard.Designer.cs
index e09ae53cec55d9dfe2ec37a12370db171065f1a1..5ef1b461065700b80cb5ac24a62b2ef5c41a480f 100644
--- a/editor source/SPNATI Character Editor/Activities/BanterWizard.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/BanterWizard.Designer.cs	
@@ -29,43 +29,58 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
 			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
-			this.cmdFilter = new System.Windows.Forms.Button();
-			this.cmdLoadTags = new System.Windows.Forms.Button();
-			this.lstTags = new System.Windows.Forms.ListBox();
-			this.lblTags = new System.Windows.Forms.Label();
-			this.lstCharacters = new System.Windows.Forms.ListBox();
-			this.lblCharacters = new System.Windows.Forms.Label();
+			this.cmdFilter = new Desktop.Skinning.SkinnedButton();
+			this.cmdLoadTags = new Desktop.Skinning.SkinnedButton();
+			this.lstTags = new Desktop.Skinning.SkinnedListBox();
+			this.lblTags = new Desktop.Skinning.SkinnedLabel();
+			this.lstCharacters = new Desktop.Skinning.SkinnedListBox();
+			this.lblCharacters = new Desktop.Skinning.SkinnedLabel();
+			this.panelLoad = new Desktop.Skinning.SkinnedPanel();
+			this.lblProgress = new Desktop.Skinning.SkinnedLabel();
+			this.progress = new Desktop.Skinning.SkinnedProgressBar();
 			this.splitContainer2 = new System.Windows.Forms.SplitContainer();
-			this.lblNoMatches = new System.Windows.Forms.Label();
-			this.lblCaseInfo = new System.Windows.Forms.Label();
-			this.cmdCreateResponse = new System.Windows.Forms.Button();
-			this.lblLines = new System.Windows.Forms.Label();
-			this.gridLines = new System.Windows.Forms.DataGridView();
+			this.grpLines = new Desktop.Skinning.SkinnedGroupBox();
+			this.gridLines = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColText = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColStage = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColCase = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.lblNoMatches = new Desktop.Skinning.SkinnedLabel();
+			this.cmdCreateResponse = new Desktop.Skinning.SkinnedButton();
+			this.lblCaseInfo = new Desktop.Skinning.SkinnedLabel();
 			this.splitContainer3 = new System.Windows.Forms.SplitContainer();
-			this.lblBasicText = new System.Windows.Forms.Label();
-			this.lstBasicLines = new System.Windows.Forms.ListBox();
-			this.lblBaseLine = new System.Windows.Forms.Label();
-			this.label3 = new System.Windows.Forms.Label();
-			this.lblResponse = new System.Windows.Forms.Label();
+			this.grpBaseLine = new Desktop.Skinning.SkinnedGroupBox();
+			this.lstBasicLines = new Desktop.Skinning.SkinnedListBox();
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.lblBasicText = new Desktop.Skinning.SkinnedLabel();
+			this.grpResponse = new Desktop.Skinning.SkinnedGroupBox();
+			this.cmdDiscard = new Desktop.Skinning.SkinnedButton();
+			this.cmdAccept = new Desktop.Skinning.SkinnedButton();
+			this.cmdJump = new Desktop.Skinning.SkinnedButton();
 			this.gridResponse = new SPNATI_Character_Editor.Controls.DialogueGrid();
+			this.ctlResponse = new SPNATI_Character_Editor.Controls.CaseControl();
+			this.skinnedLabel2 = new Desktop.Skinning.SkinnedLabel();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
 			this.splitContainer1.Panel1.SuspendLayout();
 			this.splitContainer1.Panel2.SuspendLayout();
 			this.splitContainer1.SuspendLayout();
+			this.panelLoad.SuspendLayout();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit();
 			this.splitContainer2.Panel1.SuspendLayout();
 			this.splitContainer2.Panel2.SuspendLayout();
 			this.splitContainer2.SuspendLayout();
+			this.grpLines.SuspendLayout();
 			((System.ComponentModel.ISupportInitialize)(this.gridLines)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).BeginInit();
 			this.splitContainer3.Panel1.SuspendLayout();
 			this.splitContainer3.Panel2.SuspendLayout();
 			this.splitContainer3.SuspendLayout();
+			this.grpBaseLine.SuspendLayout();
+			this.grpResponse.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// splitContainer1
@@ -86,6 +101,7 @@
 			// splitContainer1.Panel2
 			// 
 			this.splitContainer1.Panel2.Controls.Add(this.splitContainer2);
+			this.splitContainer1.Panel2.Controls.Add(this.panelLoad);
 			this.splitContainer1.Size = new System.Drawing.Size(1161, 674);
 			this.splitContainer1.SplitterDistance = 206;
 			this.splitContainer1.TabIndex = 1;
@@ -94,6 +110,9 @@
 			// 
 			this.cmdFilter.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdFilter.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdFilter.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdFilter.Flat = false;
 			this.cmdFilter.Location = new System.Drawing.Point(12, 25);
 			this.cmdFilter.Name = "cmdFilter";
 			this.cmdFilter.Size = new System.Drawing.Size(184, 23);
@@ -107,6 +126,9 @@
 			// 
 			this.cmdLoadTags.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdLoadTags.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdLoadTags.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdLoadTags.Flat = false;
 			this.cmdLoadTags.Location = new System.Drawing.Point(12, 375);
 			this.cmdLoadTags.Name = "cmdLoadTags";
 			this.cmdLoadTags.Size = new System.Drawing.Size(184, 23);
@@ -120,6 +142,9 @@
 			this.lstTags.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstTags.BackColor = System.Drawing.Color.White;
+			this.lstTags.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstTags.ForeColor = System.Drawing.Color.Black;
 			this.lstTags.FormattingEnabled = true;
 			this.lstTags.Location = new System.Drawing.Point(12, 375);
 			this.lstTags.Name = "lstTags";
@@ -131,6 +156,10 @@
 			// lblTags
 			// 
 			this.lblTags.AutoSize = true;
+			this.lblTags.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblTags.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblTags.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblTags.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.lblTags.Location = new System.Drawing.Point(12, 359);
 			this.lblTags.Name = "lblTags";
 			this.lblTags.Size = new System.Drawing.Size(136, 13);
@@ -141,6 +170,9 @@
 			// 
 			this.lstCharacters.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstCharacters.BackColor = System.Drawing.Color.White;
+			this.lstCharacters.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstCharacters.ForeColor = System.Drawing.Color.Black;
 			this.lstCharacters.FormattingEnabled = true;
 			this.lstCharacters.Location = new System.Drawing.Point(12, 51);
 			this.lstCharacters.Name = "lstCharacters";
@@ -151,12 +183,50 @@
 			// lblCharacters
 			// 
 			this.lblCharacters.AutoSize = true;
+			this.lblCharacters.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblCharacters.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblCharacters.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblCharacters.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.lblCharacters.Location = new System.Drawing.Point(12, 9);
 			this.lblCharacters.Name = "lblCharacters";
 			this.lblCharacters.Size = new System.Drawing.Size(126, 13);
 			this.lblCharacters.TabIndex = 0;
 			this.lblCharacters.Text = "Characters that target {0}";
 			// 
+			// panelLoad
+			// 
+			this.panelLoad.Controls.Add(this.lblProgress);
+			this.panelLoad.Controls.Add(this.progress);
+			this.panelLoad.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.panelLoad.Location = new System.Drawing.Point(0, 0);
+			this.panelLoad.Name = "panelLoad";
+			this.panelLoad.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.panelLoad.Size = new System.Drawing.Size(951, 674);
+			this.panelLoad.TabIndex = 5;
+			this.panelLoad.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// lblProgress
+			// 
+			this.lblProgress.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
+			this.lblProgress.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.lblProgress.ForeColor = System.Drawing.Color.Blue;
+			this.lblProgress.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblProgress.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.lblProgress.Location = new System.Drawing.Point(80, 292);
+			this.lblProgress.Name = "lblProgress";
+			this.lblProgress.Size = new System.Drawing.Size(784, 23);
+			this.lblProgress.TabIndex = 1;
+			this.lblProgress.Text = "Scanning ...";
+			this.lblProgress.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+			// 
+			// progress
+			// 
+			this.progress.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
+			this.progress.Location = new System.Drawing.Point(80, 322);
+			this.progress.Name = "progress";
+			this.progress.Size = new System.Drawing.Size(784, 23);
+			this.progress.TabIndex = 0;
+			// 
 			// splitContainer2
 			// 
 			this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
@@ -166,11 +236,8 @@
 			// 
 			// splitContainer2.Panel1
 			// 
-			this.splitContainer2.Panel1.Controls.Add(this.lblNoMatches);
+			this.splitContainer2.Panel1.Controls.Add(this.grpLines);
 			this.splitContainer2.Panel1.Controls.Add(this.lblCaseInfo);
-			this.splitContainer2.Panel1.Controls.Add(this.cmdCreateResponse);
-			this.splitContainer2.Panel1.Controls.Add(this.lblLines);
-			this.splitContainer2.Panel1.Controls.Add(this.gridLines);
 			// 
 			// splitContainer2.Panel2
 			// 
@@ -179,60 +246,66 @@
 			this.splitContainer2.SplitterDistance = 272;
 			this.splitContainer2.TabIndex = 0;
 			// 
-			// lblNoMatches
-			// 
-			this.lblNoMatches.AutoSize = true;
-			this.lblNoMatches.Location = new System.Drawing.Point(3, 25);
-			this.lblNoMatches.Name = "lblNoMatches";
-			this.lblNoMatches.Size = new System.Drawing.Size(63, 13);
-			this.lblNoMatches.TabIndex = 4;
-			this.lblNoMatches.Text = "None found";
-			this.lblNoMatches.Visible = false;
-			// 
-			// lblCaseInfo
-			// 
-			this.lblCaseInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-			this.lblCaseInfo.AutoSize = true;
-			this.lblCaseInfo.Location = new System.Drawing.Point(3, 251);
-			this.lblCaseInfo.Name = "lblCaseInfo";
-			this.lblCaseInfo.Size = new System.Drawing.Size(0, 13);
-			this.lblCaseInfo.TabIndex = 3;
+			// grpLines
 			// 
-			// cmdCreateResponse
-			// 
-			this.cmdCreateResponse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdCreateResponse.Location = new System.Drawing.Point(832, 246);
-			this.cmdCreateResponse.Name = "cmdCreateResponse";
-			this.cmdCreateResponse.Size = new System.Drawing.Size(116, 23);
-			this.cmdCreateResponse.TabIndex = 0;
-			this.cmdCreateResponse.Text = "Create Response";
-			this.cmdCreateResponse.UseVisualStyleBackColor = true;
-			this.cmdCreateResponse.Click += new System.EventHandler(this.cmdCreateResponse_Click);
-			// 
-			// lblLines
-			// 
-			this.lblLines.AutoSize = true;
-			this.lblLines.Location = new System.Drawing.Point(3, 9);
-			this.lblLines.Name = "lblLines";
-			this.lblLines.Size = new System.Drawing.Size(32, 13);
-			this.lblLines.TabIndex = 2;
-			this.lblLines.Text = "Lines";
+			this.grpLines.Controls.Add(this.gridLines);
+			this.grpLines.Controls.Add(this.lblNoMatches);
+			this.grpLines.Controls.Add(this.cmdCreateResponse);
+			this.grpLines.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.grpLines.Location = new System.Drawing.Point(0, 0);
+			this.grpLines.Name = "grpLines";
+			this.grpLines.Size = new System.Drawing.Size(951, 272);
+			this.grpLines.TabIndex = 5;
+			this.grpLines.TabStop = false;
+			this.grpLines.Text = "Lines";
 			// 
 			// gridLines
 			// 
 			this.gridLines.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridLines.BackgroundColor = System.Drawing.Color.White;
+			this.gridLines.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridLines.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridLines.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
 			this.gridLines.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridLines.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColText,
             this.ColStage,
             this.ColCase});
-			this.gridLines.Location = new System.Drawing.Point(3, 25);
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle2.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridLines.DefaultCellStyle = dataGridViewCellStyle2;
+			this.gridLines.EnableHeadersVisualStyles = false;
+			this.gridLines.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridLines.GridColor = System.Drawing.Color.LightGray;
+			this.gridLines.Location = new System.Drawing.Point(6, 25);
 			this.gridLines.MultiSelect = false;
 			this.gridLines.Name = "gridLines";
 			this.gridLines.ReadOnly = true;
-			this.gridLines.Size = new System.Drawing.Size(945, 216);
+			this.gridLines.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle3.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridLines.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
+			this.gridLines.Size = new System.Drawing.Size(939, 212);
 			this.gridLines.TabIndex = 0;
 			this.gridLines.CellEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridLines_CellEnter);
 			// 
@@ -257,6 +330,47 @@
 			this.ColCase.ReadOnly = true;
 			this.ColCase.Width = 150;
 			// 
+			// lblNoMatches
+			// 
+			this.lblNoMatches.AutoSize = true;
+			this.lblNoMatches.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.lblNoMatches.ForeColor = System.Drawing.Color.Red;
+			this.lblNoMatches.Highlight = Desktop.Skinning.SkinnedHighlight.Bad;
+			this.lblNoMatches.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.lblNoMatches.Location = new System.Drawing.Point(6, 28);
+			this.lblNoMatches.Name = "lblNoMatches";
+			this.lblNoMatches.Size = new System.Drawing.Size(93, 21);
+			this.lblNoMatches.TabIndex = 4;
+			this.lblNoMatches.Text = "None found";
+			this.lblNoMatches.Visible = false;
+			// 
+			// cmdCreateResponse
+			// 
+			this.cmdCreateResponse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCreateResponse.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCreateResponse.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdCreateResponse.Flat = false;
+			this.cmdCreateResponse.Location = new System.Drawing.Point(804, 243);
+			this.cmdCreateResponse.Name = "cmdCreateResponse";
+			this.cmdCreateResponse.Size = new System.Drawing.Size(141, 23);
+			this.cmdCreateResponse.TabIndex = 0;
+			this.cmdCreateResponse.Text = "Create Response";
+			this.cmdCreateResponse.UseVisualStyleBackColor = true;
+			this.cmdCreateResponse.Click += new System.EventHandler(this.cmdCreateResponse_Click);
+			// 
+			// lblCaseInfo
+			// 
+			this.lblCaseInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.lblCaseInfo.AutoSize = true;
+			this.lblCaseInfo.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblCaseInfo.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblCaseInfo.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblCaseInfo.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblCaseInfo.Location = new System.Drawing.Point(3, 251);
+			this.lblCaseInfo.Name = "lblCaseInfo";
+			this.lblCaseInfo.Size = new System.Drawing.Size(0, 13);
+			this.lblCaseInfo.TabIndex = 3;
+			// 
 			// splitContainer3
 			// 
 			this.splitContainer3.Dock = System.Windows.Forms.DockStyle.Fill;
@@ -265,77 +379,167 @@
 			// 
 			// splitContainer3.Panel1
 			// 
+			this.splitContainer3.Panel1.Controls.Add(this.grpBaseLine);
 			this.splitContainer3.Panel1.Controls.Add(this.lblBasicText);
-			this.splitContainer3.Panel1.Controls.Add(this.lstBasicLines);
-			this.splitContainer3.Panel1.Controls.Add(this.lblBaseLine);
 			// 
 			// splitContainer3.Panel2
 			// 
-			this.splitContainer3.Panel2.Controls.Add(this.label3);
-			this.splitContainer3.Panel2.Controls.Add(this.lblResponse);
-			this.splitContainer3.Panel2.Controls.Add(this.gridResponse);
+			this.splitContainer3.Panel2.Controls.Add(this.grpResponse);
 			this.splitContainer3.Size = new System.Drawing.Size(951, 398);
 			this.splitContainer3.SplitterDistance = 440;
 			this.splitContainer3.TabIndex = 6;
 			// 
-			// lblBasicText
+			// grpBaseLine
 			// 
-			this.lblBasicText.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblBasicText.AutoSize = true;
-			this.lblBasicText.Location = new System.Drawing.Point(174, 0);
-			this.lblBasicText.Name = "lblBasicText";
-			this.lblBasicText.Size = new System.Drawing.Size(0, 13);
-			this.lblBasicText.TabIndex = 6;
+			this.grpBaseLine.Controls.Add(this.lstBasicLines);
+			this.grpBaseLine.Controls.Add(this.skinnedLabel1);
+			this.grpBaseLine.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.grpBaseLine.Location = new System.Drawing.Point(0, 0);
+			this.grpBaseLine.Name = "grpBaseLine";
+			this.grpBaseLine.Size = new System.Drawing.Size(440, 398);
+			this.grpBaseLine.TabIndex = 8;
+			this.grpBaseLine.TabStop = false;
+			this.grpBaseLine.Text = "Response";
 			// 
 			// lstBasicLines
 			// 
 			this.lstBasicLines.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstBasicLines.BackColor = System.Drawing.Color.White;
+			this.lstBasicLines.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstBasicLines.ForeColor = System.Drawing.Color.Black;
 			this.lstBasicLines.FormattingEnabled = true;
-			this.lstBasicLines.Location = new System.Drawing.Point(6, 16);
+			this.lstBasicLines.IntegralHeight = false;
+			this.lstBasicLines.Location = new System.Drawing.Point(6, 52);
 			this.lstBasicLines.Name = "lstBasicLines";
-			this.lstBasicLines.Size = new System.Drawing.Size(431, 368);
+			this.lstBasicLines.Size = new System.Drawing.Size(427, 342);
 			this.lstBasicLines.TabIndex = 7;
 			// 
-			// lblBaseLine
-			// 
-			this.lblBaseLine.AutoSize = true;
-			this.lblBaseLine.Location = new System.Drawing.Point(3, 0);
-			this.lblBaseLine.Name = "lblBaseLine";
-			this.lblBaseLine.Size = new System.Drawing.Size(154, 13);
-			this.lblBaseLine.TabIndex = 6;
-			this.lblBaseLine.Text = "{0} is responding to these lines:";
+			// skinnedLabel1
 			// 
-			// label3
+			this.skinnedLabel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel1.ForeColor = System.Drawing.Color.Red;
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Bad;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel1.Location = new System.Drawing.Point(7, 23);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(426, 35);
+			this.skinnedLabel1.TabIndex = 8;
+			this.skinnedLabel1.Text = "Disclaimer: These are estimates. Actual results in game may vary depending on the" +
+    " game state.";
 			// 
-			this.label3.AutoSize = true;
-			this.label3.Location = new System.Drawing.Point(3, 0);
-			this.label3.Name = "label3";
-			this.label3.Size = new System.Drawing.Size(60, 13);
-			this.label3.TabIndex = 3;
-			this.label3.Text = "Responses";
+			// lblBasicText
 			// 
-			// lblResponse
+			this.lblBasicText.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.lblBasicText.AutoSize = true;
+			this.lblBasicText.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblBasicText.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblBasicText.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblBasicText.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblBasicText.Location = new System.Drawing.Point(174, 0);
+			this.lblBasicText.Name = "lblBasicText";
+			this.lblBasicText.Size = new System.Drawing.Size(0, 13);
+			this.lblBasicText.TabIndex = 6;
 			// 
-			this.lblResponse.AutoSize = true;
-			this.lblResponse.Location = new System.Drawing.Point(105, 0);
-			this.lblResponse.Name = "lblResponse";
-			this.lblResponse.Size = new System.Drawing.Size(13, 13);
-			this.lblResponse.TabIndex = 5;
-			this.lblResponse.Text = "a";
+			// grpResponse
+			// 
+			this.grpResponse.Controls.Add(this.cmdDiscard);
+			this.grpResponse.Controls.Add(this.cmdAccept);
+			this.grpResponse.Controls.Add(this.cmdJump);
+			this.grpResponse.Controls.Add(this.gridResponse);
+			this.grpResponse.Controls.Add(this.ctlResponse);
+			this.grpResponse.Controls.Add(this.skinnedLabel2);
+			this.grpResponse.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.grpResponse.Location = new System.Drawing.Point(0, 0);
+			this.grpResponse.Name = "grpResponse";
+			this.grpResponse.Size = new System.Drawing.Size(507, 398);
+			this.grpResponse.TabIndex = 6;
+			this.grpResponse.TabStop = false;
+			this.grpResponse.Text = "Write Response";
+			// 
+			// cmdDiscard
+			// 
+			this.cmdDiscard.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdDiscard.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdDiscard.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdDiscard.Flat = true;
+			this.cmdDiscard.ForeColor = System.Drawing.Color.Blue;
+			this.cmdDiscard.Location = new System.Drawing.Point(359, 369);
+			this.cmdDiscard.Name = "cmdDiscard";
+			this.cmdDiscard.Size = new System.Drawing.Size(141, 23);
+			this.cmdDiscard.TabIndex = 13;
+			this.cmdDiscard.Text = "Discard";
+			this.cmdDiscard.UseVisualStyleBackColor = true;
+			this.cmdDiscard.Click += new System.EventHandler(this.cmdDiscard_Click);
+			// 
+			// cmdAccept
+			// 
+			this.cmdAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdAccept.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdAccept.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdAccept.Flat = false;
+			this.cmdAccept.Location = new System.Drawing.Point(212, 369);
+			this.cmdAccept.Name = "cmdAccept";
+			this.cmdAccept.Size = new System.Drawing.Size(141, 23);
+			this.cmdAccept.TabIndex = 12;
+			this.cmdAccept.Text = "Accept";
+			this.cmdAccept.UseVisualStyleBackColor = true;
+			this.cmdAccept.Click += new System.EventHandler(this.cmdAccept_Click);
+			// 
+			// cmdJump
+			// 
+			this.cmdJump.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdJump.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdJump.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdJump.Flat = false;
+			this.cmdJump.Location = new System.Drawing.Point(65, 369);
+			this.cmdJump.Name = "cmdJump";
+			this.cmdJump.Size = new System.Drawing.Size(141, 23);
+			this.cmdJump.TabIndex = 11;
+			this.cmdJump.Text = "Edit Full Screen";
+			this.cmdJump.UseVisualStyleBackColor = true;
+			this.cmdJump.Click += new System.EventHandler(this.cmdJump_Click);
 			// 
 			// gridResponse
 			// 
 			this.gridResponse.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.gridResponse.Location = new System.Drawing.Point(6, 16);
+			this.gridResponse.Location = new System.Drawing.Point(9, 52);
 			this.gridResponse.Name = "gridResponse";
 			this.gridResponse.ReadOnly = false;
-			this.gridResponse.Size = new System.Drawing.Size(498, 379);
+			this.gridResponse.Size = new System.Drawing.Size(492, 311);
 			this.gridResponse.TabIndex = 0;
 			this.gridResponse.HighlightRow += new System.EventHandler<int>(this.gridResponse_HighlightRow);
+			// 
+			// ctlResponse
+			// 
+			this.ctlResponse.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.ctlResponse.Location = new System.Drawing.Point(9, 52);
+			this.ctlResponse.Name = "ctlResponse";
+			this.ctlResponse.Size = new System.Drawing.Size(492, 311);
+			this.ctlResponse.TabIndex = 10;
+			this.ctlResponse.HighlightRow += new System.EventHandler<int>(this.gridResponse_HighlightRow);
+			// 
+			// skinnedLabel2
+			// 
+			this.skinnedLabel2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedLabel2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel2.ForeColor = System.Drawing.Color.Red;
+			this.skinnedLabel2.Highlight = Desktop.Skinning.SkinnedHighlight.Bad;
+			this.skinnedLabel2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel2.Location = new System.Drawing.Point(6, 23);
+			this.skinnedLabel2.Name = "skinnedLabel2";
+			this.skinnedLabel2.Size = new System.Drawing.Size(495, 26);
+			this.skinnedLabel2.TabIndex = 9;
+			this.skinnedLabel2.Text = "These play at the same time as the selected line above, so be careful not to over" +
+    "write the line that is giving context to what the opponent is saying!";
 			// 
 			// BanterWizard
 			// 
@@ -349,18 +553,22 @@
 			this.splitContainer1.Panel2.ResumeLayout(false);
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
 			this.splitContainer1.ResumeLayout(false);
+			this.panelLoad.ResumeLayout(false);
 			this.splitContainer2.Panel1.ResumeLayout(false);
 			this.splitContainer2.Panel1.PerformLayout();
 			this.splitContainer2.Panel2.ResumeLayout(false);
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit();
 			this.splitContainer2.ResumeLayout(false);
+			this.grpLines.ResumeLayout(false);
+			this.grpLines.PerformLayout();
 			((System.ComponentModel.ISupportInitialize)(this.gridLines)).EndInit();
 			this.splitContainer3.Panel1.ResumeLayout(false);
 			this.splitContainer3.Panel1.PerformLayout();
 			this.splitContainer3.Panel2.ResumeLayout(false);
-			this.splitContainer3.Panel2.PerformLayout();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit();
 			this.splitContainer3.ResumeLayout(false);
+			this.grpBaseLine.ResumeLayout(false);
+			this.grpResponse.ResumeLayout(false);
 			this.ResumeLayout(false);
 
 		}
@@ -368,28 +576,36 @@
 		#endregion
 
 		private System.Windows.Forms.SplitContainer splitContainer1;
-		private System.Windows.Forms.ListBox lstTags;
-		private System.Windows.Forms.Label lblTags;
-		private System.Windows.Forms.ListBox lstCharacters;
-		private System.Windows.Forms.Label lblCharacters;
+		private Desktop.Skinning.SkinnedListBox lstTags;
+		private Desktop.Skinning.SkinnedLabel lblTags;
+		private Desktop.Skinning.SkinnedListBox lstCharacters;
+		private Desktop.Skinning.SkinnedLabel lblCharacters;
 		private System.Windows.Forms.SplitContainer splitContainer2;
-		private System.Windows.Forms.Label lblCaseInfo;
-		private System.Windows.Forms.Button cmdCreateResponse;
-		private System.Windows.Forms.Label lblLines;
-		private System.Windows.Forms.DataGridView gridLines;
+		private Desktop.Skinning.SkinnedLabel lblCaseInfo;
+		private Desktop.Skinning.SkinnedButton cmdCreateResponse;
+		private Desktop.Skinning.SkinnedDataGridView gridLines;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColText;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColStage;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColCase;
 		private System.Windows.Forms.SplitContainer splitContainer3;
-		private System.Windows.Forms.Label lblBasicText;
-		private System.Windows.Forms.ListBox lstBasicLines;
-		private System.Windows.Forms.Label lblBaseLine;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.Label lblResponse;
-		private Controls.DialogueGrid gridResponse;
-		private System.Windows.Forms.Button cmdLoadTags;
-		private System.Windows.Forms.Label lblNoMatches;
-		private System.Windows.Forms.Button cmdFilter;
+		private Desktop.Skinning.SkinnedLabel lblBasicText;
+		private Desktop.Skinning.SkinnedListBox lstBasicLines;
+		private Desktop.Skinning.SkinnedButton cmdLoadTags;
+		private Desktop.Skinning.SkinnedLabel lblNoMatches;
+		private Desktop.Skinning.SkinnedButton cmdFilter;
 		private System.Windows.Forms.ToolTip toolTip1;
+		private Desktop.Skinning.SkinnedGroupBox grpLines;
+		private Desktop.Skinning.SkinnedGroupBox grpBaseLine;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
+		private Desktop.Skinning.SkinnedGroupBox grpResponse;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel2;
+		private Controls.CaseControl ctlResponse;
+		private Desktop.Skinning.SkinnedButton cmdDiscard;
+		private Desktop.Skinning.SkinnedButton cmdAccept;
+		private Desktop.Skinning.SkinnedButton cmdJump;
+		private Controls.DialogueGrid gridResponse;
+		private Desktop.Skinning.SkinnedPanel panelLoad;
+		private Desktop.Skinning.SkinnedProgressBar progress;
+		private Desktop.Skinning.SkinnedLabel lblProgress;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/BanterWizard.cs b/editor source/SPNATI Character Editor/Activities/BanterWizard.cs
index d4f0fd325642f679d2939f3ee5d22d6caf8c5ec2..c48eaa2dc4c00eade9019f81490124132b21f106 100644
--- a/editor source/SPNATI Character Editor/Activities/BanterWizard.cs	
+++ b/editor source/SPNATI Character Editor/Activities/BanterWizard.cs	
@@ -1,7 +1,7 @@
 using Desktop;
-using Desktop.Messaging;
 using System;
 using System.Collections.Generic;
+using System.Threading.Tasks;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Activities
@@ -12,12 +12,14 @@ namespace SPNATI_Character_Editor.Activities
 	{
 		private Character _character;
 		private TargetData _selectedData;
-		private int _currentLine;
 		private Case _workingResponse;
 		public bool Modified { get; private set; }
 		private Dictionary<Character, List<TargetData>> _lines = new Dictionary<Character, List<TargetData>>();
 		private Dictionary<Character, List<TargetData>> _filterLines = new Dictionary<Character, List<TargetData>>();
 		private ImageLibrary _imageLibrary;
+		private int _oldSplitter;
+		private bool _editing;
+		private bool _loading;
 
 		public BanterWizard()
 		{
@@ -40,6 +42,7 @@ namespace SPNATI_Character_Editor.Activities
 
 		protected override void OnFirstActivate()
 		{
+			ctlResponse.SetCharacter(_character);
 			HideResponses();
 			lblCharacters.Text = string.Format(lblCharacters.Text, _character);
 			lstCharacters.Sorted = true;
@@ -58,6 +61,24 @@ namespace SPNATI_Character_Editor.Activities
 			}
 		}
 
+		public override bool CanQuit(CloseArgs args)
+		{
+			if (_loading)
+			{
+				return false;
+			}
+			return base.CanQuit(args);
+		}
+
+		public override bool CanDeactivate(DeactivateArgs args)
+		{
+			if (_loading)
+			{
+				return false;
+			}
+			return base.CanDeactivate(args);
+		}
+
 		private void lstCharacters_SelectedIndexChanged(object sender, EventArgs e)
 		{
 			ShowTargetedLines(lstCharacters.SelectedItem as Character, _lines, TargetType.DirectTarget);
@@ -72,6 +93,7 @@ namespace SPNATI_Character_Editor.Activities
 
 		private List<TargetData> LoadLines(Character other, TargetType targetType)
 		{
+			other.Behavior.FlagTemporary();
 			List<TargetData> lines = new List<TargetData>();
 			foreach (var stageCase in other.GetWorkingCasesTargetedAtCharacter(_character, targetType))
 			{
@@ -107,6 +129,7 @@ namespace SPNATI_Character_Editor.Activities
 				TargetData data = new TargetData(other, lastCase);
 				lines.Add(data);
 			}
+			other.Behavior.ReleaseTemporary();
 			return lines;
 		}
 
@@ -114,7 +137,7 @@ namespace SPNATI_Character_Editor.Activities
 		{
 			if (other == null)
 				return;
-			lblLines.Text = "Lines spoken by " + other;
+			grpLines.Text = "Lines spoken by " + other;
 			HideResponses();
 			List<TargetData> lines = targetedLines.GetOrAddDefault(other, () =>
 			{
@@ -152,6 +175,7 @@ namespace SPNATI_Character_Editor.Activities
 			if (targetedCase.Target != null)
 			{
 				target = CharacterDatabase.Get(targetedCase.Target);
+				if (target == null) { return "???"; }
 			}
 
 			if (trigger.Tag.Contains("_removing_"))
@@ -186,7 +210,6 @@ namespace SPNATI_Character_Editor.Activities
 
 		private void gridLines_CellEnter(object sender, DataGridViewCellEventArgs e)
 		{
-			_currentLine = e.RowIndex;
 			SelectLine(e.RowIndex);
 		}
 
@@ -200,7 +223,7 @@ namespace SPNATI_Character_Editor.Activities
 				_selectedData = data;
 				lblCaseInfo.Text = data.Case.ToString();
 
-				lblBaseLine.Text = string.Format("{0} is reacting to these lines from {1}:", _selectedData.Character, _character);
+				grpBaseLine.Text = string.Format("{0} may be reacting to these lines from {1}:", _selectedData.Character, _character);
 
 				ImageLibrary library = ImageLibrary.Get(_selectedData.Character);
 				DialogueLine line = row.Cells["ColText"].Tag as DialogueLine;
@@ -231,7 +254,7 @@ namespace SPNATI_Character_Editor.Activities
 						if (workingCase.MatchesConditions(sampleResponse))
 						{
 							//Found one
-							ShowResponse(workingCase);
+							ShowResponse(workingCase, false);
 							hasResponses = true;
 							break;
 						}
@@ -283,7 +306,15 @@ namespace SPNATI_Character_Editor.Activities
 		{
 			if (index == -1)
 				return;
-			string image = gridResponse.GetImage(index);
+			string image = "";
+			if (ctlResponse.Visible)
+			{
+				image = ctlResponse.GetImage(index);
+			}
+			else
+			{
+				image = gridResponse.GetImage(index);
+			}
 			CharacterImage img = null;
 			img = _imageLibrary.Find(image);
 			if (img == null)
@@ -297,26 +328,12 @@ namespace SPNATI_Character_Editor.Activities
 
 		private void cmdCreateResponse_Click(object sender, EventArgs e)
 		{
-			if (_workingResponse != null)
-			{
-				DeleteResponse();
-			}
-			else
+			if (_workingResponse == null)
 			{
 				CreateResponse();
 			}
 		}
-
-		private void DeleteResponse()
-		{
-			if (_workingResponse == null)
-				return;
-			Modified = true;
-			_character.Behavior.RemoveWorkingCase(_workingResponse);
-			HideResponses();
-			SelectLine(_currentLine);
-		}
-
+		
 		private void CreateResponse()
 		{
 			if (_selectedData == null)
@@ -325,8 +342,7 @@ namespace SPNATI_Character_Editor.Activities
 			Case response = _selectedData.Case.CreateResponse(_selectedData.Character, _character);
 			if (response == null)
 				return;
-			_character.Behavior.AddWorkingCase(response);
-			ShowResponse(response);
+			ShowResponse(response, true);
 		}
 
 		private void HideResponses()
@@ -336,14 +352,24 @@ namespace SPNATI_Character_Editor.Activities
 			splitContainer3.Panel1Collapsed = false;
 			if (_workingResponse != null)
 			{
-				gridResponse.Save();
-				gridResponse.Clear();
+				if (gridResponse.Visible)
+				{
+					gridResponse.Save();
+					gridResponse.Clear();
+				}
+				else if (_editing)
+				{
+					ctlResponse.SetCase(null, null);
+					_editing = false;
+					gridLines.Enabled = true;
+					splitContainer2.SplitterDistance = _oldSplitter;
+				}
 				_workingResponse = null;
 				_selectedData = null;
 			}
 		}
 
-		private void ShowResponse(Case response)
+		private void ShowResponse(Case response, bool editing)
 		{
 			HashSet<int> selectedStages = new HashSet<int>();
 			foreach (int stage in response.Stages)
@@ -351,12 +377,37 @@ namespace SPNATI_Character_Editor.Activities
 				selectedStages.Add(stage);
 			}
 			_workingResponse = response;
-			gridResponse.SetData(_character, _character.Behavior.Stages.Find(s => s.Id == response.Stages[0]), response, selectedStages, _imageLibrary);
-			lblResponse.Text = response.ToString();
+			if (editing)
+			{
+				gridResponse.Visible = false;
+				ctlResponse.Visible = true;
+				ctlResponse.SetCase(new Stage(response.Stages[0]), response);
+			}
+			else
+			{
+				gridResponse.Visible = true;
+				ctlResponse.Visible = false;
+				gridResponse.SetData(_character, _character.Behavior.Stages.Find(s => s.Id == response.Stages[0]), response, selectedStages, _imageLibrary);
+			}			
+			
+			grpResponse.Text = $"Response from {_character}";
 			splitContainer3.Panel2Collapsed = false;
 			splitContainer3.Panel1Collapsed = true;
+			cmdAccept.Enabled = editing;
+			cmdDiscard.Enabled = editing;
 
-			cmdCreateResponse.Text = "Delete Response";
+			cmdCreateResponse.Enabled = false;
+			if (editing)
+			{
+				_editing = true;
+				_oldSplitter = splitContainer2.SplitterDistance;
+				splitContainer2.SplitterDistance = 110;
+				if (gridLines.SelectedRows.Count > 0)
+				{
+					gridLines.FirstDisplayedScrollingRowIndex = gridLines.SelectedRows[0].Index;
+				}
+				gridLines.Enabled = false;
+			}
 		}
 
 		private class TargetData
@@ -376,7 +427,7 @@ namespace SPNATI_Character_Editor.Activities
 		{
 			if (_workingResponse != null)
 			{
-				gridResponse.Save();
+				ctlResponse.Save();
 			}
 		}
 
@@ -411,38 +462,84 @@ namespace SPNATI_Character_Editor.Activities
 			FilterTargets();
 		}
 
-		private void FilterTargets()
+		private async void FilterTargets()
 		{
+			_loading = true;
+			splitContainer1.Panel1.Enabled = false;
+			panelLoad.Visible = true;
+			panelLoad.BringToFront();
+			int characters = CharacterDatabase.Count;
 			Cursor.Current = Cursors.WaitCursor;
 			lstCharacters.Items.Clear();
+			int count = 0;
+			progress.Maximum = characters;
 			foreach (Character other in CharacterDatabase.Characters)
 			{
+				progress.Value = count++;
 				if (other == _character || other.FolderName == "human")
 				{
 					continue;
 				}
-				if (_lines.ContainsKey(other))
+				lblProgress.Text = $"Scanning {other}...";
+				bool add = await Task.Run(() =>
 				{
-					if (_lines[other].Count > 0)
+					if (_lines.ContainsKey(other))
 					{
-						lstCharacters.Items.Add(other);
+						if (_lines[other].Count > 0)
+						{
+							return true;
+						}
 					}
-				}
-				else
-				{
-					List<TargetData> lines = LoadLines(other, TargetType.DirectTarget);
-					_lines[other] = lines;
-					if (lines.Count > 0)
+					else
 					{
-						lstCharacters.Items.Add(other);
+						List<TargetData> lines = LoadLines(other, TargetType.DirectTarget);
+						_lines[other] = lines;
+						if (lines.Count > 0)
+						{
+							return true;
+						}
 					}
+					return false;
+				});
+				if (add)
+				{
+					lstCharacters.Items.Add(other);
 				}
 			}
+			panelLoad.Visible = false;
 			int margin = lstCharacters.Top - cmdFilter.Bottom;
 			lstCharacters.Height = lstCharacters.Height + cmdFilter.Height + margin;
 			lstCharacters.Top = cmdFilter.Top;
 			cmdFilter.Visible = false;
+			splitContainer1.Panel1.Enabled = true;
 			Cursor.Current = Cursors.Default;
+			_loading = false;
+		}
+
+		private void cmdJump_Click(object sender, EventArgs e)
+		{
+			if (_editing)
+			{
+				ctlResponse.Save();
+				_character.Behavior.AddWorkingCase(_workingResponse);
+			}
+			else
+			{
+				gridResponse.Save();
+			}
+			Shell.Instance.Launch<Character, DialogueEditor>(_character, _workingResponse);
+		}
+
+		private void cmdAccept_Click(object sender, EventArgs e)
+		{
+			ctlResponse.Save();
+			_character.Behavior.AddWorkingCase(_workingResponse);
+			HideResponses();
+		}
+
+		private void cmdDiscard_Click(object sender, EventArgs e)
+		{
+			HideResponses();
 		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/BanterWizard.resx b/editor source/SPNATI Character Editor/Activities/BanterWizard.resx
index ec34a92b3770431ab12fffe5d4707535e947102b..d8d57e95164f4315a9cce279a7cb33a97f22f5e2 100644
--- a/editor source/SPNATI Character Editor/Activities/BanterWizard.resx	
+++ b/editor source/SPNATI Character Editor/Activities/BanterWizard.resx	
@@ -129,13 +129,4 @@
   <metadata name="ColCase.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
-  <metadata name="ColText.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>True</value>
-  </metadata>
-  <metadata name="ColStage.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>True</value>
-  </metadata>
-  <metadata name="ColCase.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>True</value>
-  </metadata>
 </root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Activities/CaseDataSlicer.Designer.cs b/editor source/SPNATI Character Editor/Activities/CaseDataSlicer.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a3c72ceb6bb1b4b72f8f2fa241570988899ce462
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/CaseDataSlicer.Designer.cs	
@@ -0,0 +1,57 @@
+namespace SPNATI_Character_Editor.Activities
+{
+	partial class CaseDataSlicer
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.ctlSlicer = new Desktop.CommonControls.DataSlicerControl();
+			this.SuspendLayout();
+			// 
+			// ctlSlicer
+			// 
+			this.ctlSlicer.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.ctlSlicer.Location = new System.Drawing.Point(0, 0);
+			this.ctlSlicer.Name = "ctlSlicer";
+			this.ctlSlicer.Size = new System.Drawing.Size(999, 652);
+			this.ctlSlicer.TabIndex = 0;
+			// 
+			// CaseDataSlicer
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.ctlSlicer);
+			this.Name = "CaseDataSlicer";
+			this.Size = new System.Drawing.Size(999, 652);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private Desktop.CommonControls.DataSlicerControl ctlSlicer;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Activities/CaseDataSlicer.cs b/editor source/SPNATI Character Editor/Activities/CaseDataSlicer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..92b5325126e819cda00e1d29e93260135f6b3651
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/CaseDataSlicer.cs	
@@ -0,0 +1,42 @@
+using Desktop;
+using Desktop.Reporting;
+
+namespace SPNATI_Character_Editor.Activities
+{
+	[Activity(typeof(Character), 750)]
+	public partial class CaseDataSlicer : Activity
+	{
+		private Character _character;
+
+		public CaseDataSlicer()
+		{
+			InitializeComponent();
+		}
+
+		public override string Caption
+		{
+			get { return "Line Slicer"; }
+		}
+
+		protected override void OnInitialize()
+		{
+			_character = Record as Character;
+		}
+
+		protected override void OnFirstActivate()
+		{
+			DataSlicer slicer = new DataSlicer(_character);
+			ctlSlicer.SetSlicer(slicer, _character.Behavior.GetWorkingCases());
+		}
+
+		protected override void OnActivate()
+		{
+			Workspace.ToggleSidebar(false);
+		}
+
+		protected override void OnDeactivate()
+		{
+			Workspace.ToggleSidebar(true);
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleControl.resx b/editor source/SPNATI Character Editor/Activities/CaseDataSlicer.resx
similarity index 100%
rename from editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleControl.resx
rename to editor source/SPNATI Character Editor/Activities/CaseDataSlicer.resx
diff --git a/editor source/SPNATI Character Editor/Activities/CharacterConfiguration.Designer.cs b/editor source/SPNATI Character Editor/Activities/CharacterConfiguration.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..db06086b20370c26cea038dcfe93ac5f940dc5e0
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/CharacterConfiguration.Designer.cs	
@@ -0,0 +1,128 @@
+namespace SPNATI_Character_Editor.Activities
+{
+	partial class CharacterConfiguration
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.gridPrefixes = new Desktop.Skinning.SkinnedDataGridView();
+			this.ColPrefix = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			((System.ComponentModel.ISupportInitialize)(this.gridPrefixes)).BeginInit();
+			this.SuspendLayout();
+			// 
+			// label1
+			// 
+			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(3, 3);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(316, 13);
+			this.label1.TabIndex = 0;
+			this.label1.Text = "Exclude images with these prefixes from use as poses in dialogue:";
+			// 
+			// gridPrefixes
+			// 
+			this.gridPrefixes.AllowUserToDeleteRows = false;
+			this.gridPrefixes.AllowUserToResizeColumns = false;
+			this.gridPrefixes.AllowUserToResizeRows = false;
+			this.gridPrefixes.BackgroundColor = System.Drawing.Color.White;
+			this.gridPrefixes.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridPrefixes.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridPrefixes.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
+			this.gridPrefixes.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+			this.gridPrefixes.ColumnHeadersVisible = false;
+			this.gridPrefixes.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.ColPrefix});
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle2.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridPrefixes.DefaultCellStyle = dataGridViewCellStyle2;
+			this.gridPrefixes.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
+			this.gridPrefixes.EnableHeadersVisualStyles = false;
+			this.gridPrefixes.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridPrefixes.GridColor = System.Drawing.Color.LightGray;
+			this.gridPrefixes.Location = new System.Drawing.Point(6, 19);
+			this.gridPrefixes.Name = "gridPrefixes";
+			this.gridPrefixes.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle3.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridPrefixes.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
+			this.gridPrefixes.RowHeadersVisible = false;
+			this.gridPrefixes.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
+			this.gridPrefixes.Size = new System.Drawing.Size(313, 320);
+			this.gridPrefixes.TabIndex = 1;
+			// 
+			// ColPrefix
+			// 
+			this.ColPrefix.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.ColPrefix.HeaderText = "Prefix";
+			this.ColPrefix.Name = "ColPrefix";
+			// 
+			// CharacterConfiguration
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.gridPrefixes);
+			this.Controls.Add(this.label1);
+			this.Name = "CharacterConfiguration";
+			this.Size = new System.Drawing.Size(935, 644);
+			((System.ComponentModel.ISupportInitialize)(this.gridPrefixes)).EndInit();
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedDataGridView gridPrefixes;
+		private System.Windows.Forms.DataGridViewTextBoxColumn ColPrefix;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Activities/CharacterConfiguration.cs b/editor source/SPNATI Character Editor/Activities/CharacterConfiguration.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4ec372008d2bf17292b1f205bccd0ed4c123ce94
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/CharacterConfiguration.cs	
@@ -0,0 +1,50 @@
+using Desktop;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Activities
+{
+	[Spacer]
+	[Activity(typeof(Character), 1000)]
+	public partial class CharacterConfiguration : Activity
+	{
+		private Character _character;
+		private CharacterEditorData _editorData;
+
+		public override string Caption
+		{
+			get { return "Configuration"; }
+		}
+
+		public CharacterConfiguration()
+		{
+			InitializeComponent();
+		}
+
+		protected override void OnInitialize()
+		{
+			_character = Record as Character;
+			_editorData = CharacterDatabase.GetEditorData(_character);
+		}
+
+		protected override void OnFirstActivate()
+		{
+			foreach (string prefix in _editorData.IgnoredPrefixes)
+			{
+				gridPrefixes.Rows.Add(new object[] { prefix });
+			}
+		}
+
+		public override void Save()
+		{
+			_editorData.IgnoredPrefixes.Clear();
+			foreach (DataGridViewRow row in gridPrefixes.Rows)
+			{
+				string prefix = row.Cells[0].Value?.ToString();
+				if (!string.IsNullOrEmpty(prefix))
+				{
+					_editorData.IgnoredPrefixes.Add(prefix);	
+				}
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Activities/CharacterConfiguration.resx b/editor source/SPNATI Character Editor/Activities/CharacterConfiguration.resx
new file mode 100644
index 0000000000000000000000000000000000000000..f583d95cb20af70186fe050d378d264132650951
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/CharacterConfiguration.resx	
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="ColPrefix.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Activities/CharacterPreview.Designer.cs b/editor source/SPNATI Character Editor/Activities/CharacterPreview.Designer.cs
index 395a806cce6e8da057d78bbef79ba45f575d9220..c50e1175bb2e7103dc5153b1e56622ea651f52ec 100644
--- a/editor source/SPNATI Character Editor/Activities/CharacterPreview.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/CharacterPreview.Designer.cs	
@@ -28,37 +28,45 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.lblLinesOfDialogue = new System.Windows.Forms.Label();
-			this.label4 = new System.Windows.Forms.Label();
-			this.lblSkin = new System.Windows.Forms.Label();
-			this.cboSkin = new System.Windows.Forms.ComboBox();
-			this.chkText = new System.Windows.Forms.CheckBox();
+			this.lblLinesOfDialogue = new Desktop.Skinning.SkinnedLabel();
+			this.lblSkin = new Desktop.Skinning.SkinnedLabel();
+			this.cboSkin = new Desktop.Skinning.SkinnedComboBox();
+			this.chkText = new Desktop.Skinning.SkinnedCheckBox();
+			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+			this.cmdReference = new Desktop.Skinning.SkinnedButton();
 			this.picPortrait = new SPNATI_Character_Editor.Controls.CharacterImageBox();
+			this.tabsReference = new Desktop.Skinning.SkinnedTabControl();
+			this.tabTags = new System.Windows.Forms.TabPage();
+			this.stripReference = new Desktop.Skinning.SkinnedTabStrip();
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+			this.splitContainer1.Panel1.SuspendLayout();
+			this.splitContainer1.Panel2.SuspendLayout();
+			this.splitContainer1.SuspendLayout();
+			this.tabsReference.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// lblLinesOfDialogue
 			// 
 			this.lblLinesOfDialogue.AutoSize = true;
-			this.lblLinesOfDialogue.Location = new System.Drawing.Point(129, -2);
+			this.lblLinesOfDialogue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblLinesOfDialogue.ForeColor = System.Drawing.Color.Black;
+			this.lblLinesOfDialogue.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblLinesOfDialogue.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblLinesOfDialogue.Location = new System.Drawing.Point(0, 1);
 			this.lblLinesOfDialogue.Name = "lblLinesOfDialogue";
 			this.lblLinesOfDialogue.Size = new System.Drawing.Size(13, 13);
 			this.lblLinesOfDialogue.TabIndex = 17;
 			this.lblLinesOfDialogue.Text = "0";
 			// 
-			// label4
-			// 
-			this.label4.AutoSize = true;
-			this.label4.Location = new System.Drawing.Point(0, -2);
-			this.label4.Name = "label4";
-			this.label4.Size = new System.Drawing.Size(123, 13);
-			this.label4.TabIndex = 16;
-			this.label4.Text = "Unique lines of dialogue:";
-			// 
 			// lblSkin
 			// 
-			this.lblSkin.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+			this.lblSkin.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
 			this.lblSkin.AutoSize = true;
-			this.lblSkin.Location = new System.Drawing.Point(40, 620);
+			this.lblSkin.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblSkin.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.lblSkin.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblSkin.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.lblSkin.Location = new System.Drawing.Point(115, 623);
 			this.lblSkin.Name = "lblSkin";
 			this.lblSkin.Size = new System.Drawing.Size(31, 13);
 			this.lblSkin.TabIndex = 18;
@@ -66,12 +74,20 @@
 			// 
 			// cboSkin
 			// 
-			this.cboSkin.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+			this.cboSkin.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cboSkin.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboSkin.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboSkin.BackColor = System.Drawing.Color.White;
 			this.cboSkin.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboSkin.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cboSkin.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboSkin.FormattingEnabled = true;
-			this.cboSkin.Location = new System.Drawing.Point(76, 617);
+			this.cboSkin.Location = new System.Drawing.Point(145, 619);
 			this.cboSkin.Name = "cboSkin";
-			this.cboSkin.Size = new System.Drawing.Size(121, 21);
+			this.cboSkin.SelectedIndex = -1;
+			this.cboSkin.SelectedItem = null;
+			this.cboSkin.Size = new System.Drawing.Size(105, 21);
+			this.cboSkin.Sorted = false;
 			this.cboSkin.TabIndex = 19;
 			this.cboSkin.SelectedIndexChanged += new System.EventHandler(this.cboSkin_SelectedIndexChanged);
 			// 
@@ -79,7 +95,7 @@
 			// 
 			this.chkText.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
 			this.chkText.AutoSize = true;
-			this.chkText.Location = new System.Drawing.Point(176, -1);
+			this.chkText.Location = new System.Drawing.Point(172, 0);
 			this.chkText.Name = "chkText";
 			this.chkText.Size = new System.Drawing.Size(77, 17);
 			this.chkText.TabIndex = 20;
@@ -87,40 +103,130 @@
 			this.chkText.UseVisualStyleBackColor = true;
 			this.chkText.CheckedChanged += new System.EventHandler(this.chkText_CheckedChanged);
 			// 
+			// splitContainer1
+			// 
+			this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.splitContainer1.Location = new System.Drawing.Point(0, 0);
+			this.splitContainer1.Name = "splitContainer1";
+			this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
+			// 
+			// splitContainer1.Panel1
+			// 
+			this.splitContainer1.Panel1.Controls.Add(this.cboSkin);
+			this.splitContainer1.Panel1.Controls.Add(this.chkText);
+			this.splitContainer1.Panel1.Controls.Add(this.cmdReference);
+			this.splitContainer1.Panel1.Controls.Add(this.lblLinesOfDialogue);
+			this.splitContainer1.Panel1.Controls.Add(this.lblSkin);
+			this.splitContainer1.Panel1.Controls.Add(this.picPortrait);
+			this.splitContainer1.Panel1.Resize += new System.EventHandler(this.splitContainer1_Panel1_Resize);
+			// 
+			// splitContainer1.Panel2
+			// 
+			this.splitContainer1.Panel2.Controls.Add(this.tabsReference);
+			this.splitContainer1.Panel2.Controls.Add(this.stripReference);
+			this.splitContainer1.Panel2Collapsed = true;
+			this.splitContainer1.Size = new System.Drawing.Size(251, 641);
+			this.splitContainer1.SplitterDistance = 448;
+			this.splitContainer1.TabIndex = 22;
+			// 
+			// cmdReference
+			// 
+			this.cmdReference.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.cmdReference.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdReference.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdReference.Flat = false;
+			this.cmdReference.Image = global::SPNATI_Character_Editor.Properties.Resources.ChevronUp;
+			this.cmdReference.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			this.cmdReference.Location = new System.Drawing.Point(2, 619);
+			this.cmdReference.Name = "cmdReference";
+			this.cmdReference.Size = new System.Drawing.Size(103, 21);
+			this.cmdReference.TabIndex = 21;
+			this.cmdReference.Text = "Reference";
+			this.cmdReference.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+			this.cmdReference.UseVisualStyleBackColor = true;
+			this.cmdReference.Click += new System.EventHandler(this.cmdReference_Click);
+			// 
 			// picPortrait
 			// 
-			this.picPortrait.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.picPortrait.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
 			this.picPortrait.Location = new System.Drawing.Point(0, 0);
 			this.picPortrait.Name = "picPortrait";
 			this.picPortrait.ShowTextBox = false;
-			this.picPortrait.Size = new System.Drawing.Size(251, 641);
+			this.picPortrait.Size = new System.Drawing.Size(251, 640);
 			this.picPortrait.TabIndex = 15;
 			this.picPortrait.TabStop = false;
 			// 
+			// tabsReference
+			// 
+			this.tabsReference.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.tabsReference.Controls.Add(this.tabTags);
+			this.tabsReference.Location = new System.Drawing.Point(0, 23);
+			this.tabsReference.Margin = new System.Windows.Forms.Padding(0);
+			this.tabsReference.Name = "tabsReference";
+			this.tabsReference.SelectedIndex = 0;
+			this.tabsReference.Size = new System.Drawing.Size(251, 166);
+			this.tabsReference.TabIndex = 0;
+			// 
+			// tabTags
+			// 
+			this.tabTags.BackColor = System.Drawing.Color.White;
+			this.tabTags.Location = new System.Drawing.Point(4, 22);
+			this.tabTags.Name = "tabTags";
+			this.tabTags.Padding = new System.Windows.Forms.Padding(3);
+			this.tabTags.Size = new System.Drawing.Size(243, 140);
+			this.tabTags.TabIndex = 0;
+			this.tabTags.Text = "Tags";
+			// 
+			// stripReference
+			// 
+			this.stripReference.Dock = System.Windows.Forms.DockStyle.Top;
+			this.stripReference.Location = new System.Drawing.Point(0, 0);
+			this.stripReference.Margin = new System.Windows.Forms.Padding(0);
+			this.stripReference.Name = "stripReference";
+			this.stripReference.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.stripReference.ShowCloseButton = false;
+			this.stripReference.Size = new System.Drawing.Size(150, 23);
+			this.stripReference.StartMargin = 5;
+			this.stripReference.TabControl = this.tabsReference;
+			this.stripReference.TabIndex = 1;
+			this.stripReference.TabMargin = 5;
+			this.stripReference.TabPadding = 20;
+			this.stripReference.TabSize = -1;
+			this.stripReference.TabType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.stripReference.Vertical = false;
+			// 
 			// CharacterPreview
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.chkText);
-			this.Controls.Add(this.cboSkin);
-			this.Controls.Add(this.lblSkin);
-			this.Controls.Add(this.lblLinesOfDialogue);
-			this.Controls.Add(this.label4);
-			this.Controls.Add(this.picPortrait);
+			this.Controls.Add(this.splitContainer1);
 			this.Name = "CharacterPreview";
 			this.Size = new System.Drawing.Size(251, 641);
+			this.splitContainer1.Panel1.ResumeLayout(false);
+			this.splitContainer1.Panel1.PerformLayout();
+			this.splitContainer1.Panel2.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+			this.splitContainer1.ResumeLayout(false);
+			this.tabsReference.ResumeLayout(false);
 			this.ResumeLayout(false);
-			this.PerformLayout();
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.Label lblLinesOfDialogue;
-		private System.Windows.Forms.Label label4;
+		private Desktop.Skinning.SkinnedLabel lblLinesOfDialogue;
 		private SPNATI_Character_Editor.Controls.CharacterImageBox picPortrait;
-		private System.Windows.Forms.Label lblSkin;
-		private System.Windows.Forms.ComboBox cboSkin;
-		private System.Windows.Forms.CheckBox chkText;
+		private Desktop.Skinning.SkinnedLabel lblSkin;
+		private Desktop.Skinning.SkinnedComboBox cboSkin;
+		private Desktop.Skinning.SkinnedCheckBox chkText;
+		private Desktop.Skinning.SkinnedButton cmdReference;
+		private System.Windows.Forms.SplitContainer splitContainer1;
+		private Desktop.Skinning.SkinnedTabControl tabsReference;
+		private System.Windows.Forms.TabPage tabTags;
+		private Desktop.Skinning.SkinnedTabStrip stripReference;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/CharacterPreview.cs b/editor source/SPNATI Character Editor/Activities/CharacterPreview.cs
index 6d64253856d085c45d944f599d32511918acb5d8..a3ac45060019e7a553b1975a6d5f5940d71942a2 100644
--- a/editor source/SPNATI Character Editor/Activities/CharacterPreview.cs	
+++ b/editor source/SPNATI Character Editor/Activities/CharacterPreview.cs	
@@ -1,4 +1,6 @@
 using Desktop;
+using SPNATI_Character_Editor.Controls.Reference;
+using System.Collections.Generic;
 
 namespace SPNATI_Character_Editor.Activities
 {
@@ -37,6 +39,7 @@ namespace SPNATI_Character_Editor.Activities
 			}
 			SubscribeWorkspace<DialogueLine>(WorkspaceMessages.PreviewLine, UpdatePreview);
 			SubscribeWorkspace<CharacterImage>(WorkspaceMessages.UpdatePreviewImage, UpdatePreviewImage);
+			SubscribeWorkspace<List<string>>(WorkspaceMessages.UpdateMarkers, UpdateMarkers);
 			UpdateLineCount();
 		}
 
@@ -84,17 +87,18 @@ namespace SPNATI_Character_Editor.Activities
 			picPortrait.Destroy();
 		}
 
+		private void UpdateMarkers(List<string> markers)
+		{
+			picPortrait.SetMarkers(markers);
+		}
+
 		private void UpdatePreviewImage(CharacterImage image)
 		{
-			if (Config.GetBoolean(Settings.HideImages))
-				return;
 			picPortrait.SetImage(image);
 		}
 
 		private void UpdatePreview(DialogueLine line)
 		{
-			if (Config.GetBoolean(Settings.HideImages))
-				return;
 			picPortrait.SetText(line);
 		}
 
@@ -102,18 +106,17 @@ namespace SPNATI_Character_Editor.Activities
 		{
 			if (_character != null)
 			{
-				lblLinesOfDialogue.Text = _character.Behavior.UniqueLines.ToString();
+				lblLinesOfDialogue.Text = $"Unique lines: {(_character.Behavior.UniqueLines.ToString())}";
 			}
 			else
 			{
-				label4.Visible = false;
 				lblLinesOfDialogue.Visible = false;
 			}
 		}
 
 		private void cboSkin_SelectedIndexChanged(object sender, System.EventArgs e)
 		{
-			if (_character == null) { return; } 
+			if (_character == null) { return; }
 			SkinLink current = cboSkin.SelectedItem as SkinLink;
 			_character.CurrentSkin = current?.Costume;
 
@@ -129,5 +132,30 @@ namespace SPNATI_Character_Editor.Activities
 			Config.Set("PreviewText", preview);
 			picPortrait.ShowTextBox = preview;
 		}
+
+		private void cmdReference_Click(object sender, System.EventArgs e)
+		{
+			splitContainer1.Panel2Collapsed = !splitContainer1.Panel2Collapsed;
+			cmdReference.Image = splitContainer1.Panel2Collapsed ? Properties.Resources.ChevronUp : Properties.Resources.ChevronDown;
+			splitContainer1.Panel1.Invalidate(true);
+			if (!splitContainer1.Panel2Collapsed && tabTags.Controls.Count == 0)
+			{
+				BuildReference();
+			}
+		}
+
+		private void BuildReference()
+		{
+			TagGuide guide = new TagGuide();
+			tabsReference.Width = splitContainer1.Panel2.Width;
+			tabsReference.Height = splitContainer1.Panel2.Height - stripReference.Height;
+			tabTags.Controls.Add(guide);
+			guide.Dock = System.Windows.Forms.DockStyle.Fill;
+		}
+
+		private void splitContainer1_Panel1_Resize(object sender, System.EventArgs e)
+		{
+			splitContainer1.Panel1.Invalidate(true);
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/ChartHost.Designer.cs b/editor source/SPNATI Character Editor/Activities/ChartHost.Designer.cs
index 5ed1d968dcbf27007d5bad7252937ed455a5c736..0f611a6d05473f0b7a7007536a2c32ed00ad876a 100644
--- a/editor source/SPNATI Character Editor/Activities/ChartHost.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/ChartHost.Designer.cs	
@@ -29,15 +29,17 @@
 		private void InitializeComponent()
 		{
 			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
-			this.lstGraph = new System.Windows.Forms.ListBox();
-			this.label1 = new System.Windows.Forms.Label();
+			this.lstGraph = new Desktop.Skinning.SkinnedListBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
 			this.viewPanel = new System.Windows.Forms.FlowLayoutPanel();
-			this.lblViews = new System.Windows.Forms.Label();
-			this.chartContainer = new System.Windows.Forms.Panel();
+			this.lblViews = new Desktop.Skinning.SkinnedLabel();
+			this.chartContainer = new Desktop.Skinning.SkinnedPanel();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
 			this.splitContainer1.Panel1.SuspendLayout();
 			this.splitContainer1.Panel2.SuspendLayout();
 			this.splitContainer1.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// splitContainer1
@@ -54,8 +56,7 @@
 			// 
 			// splitContainer1.Panel2
 			// 
-			this.splitContainer1.Panel2.Controls.Add(this.viewPanel);
-			this.splitContainer1.Panel2.Controls.Add(this.lblViews);
+			this.splitContainer1.Panel2.Controls.Add(this.skinnedPanel1);
 			this.splitContainer1.Panel2.Controls.Add(this.chartContainer);
 			this.splitContainer1.Size = new System.Drawing.Size(1206, 743);
 			this.splitContainer1.SplitterDistance = 215;
@@ -66,35 +67,58 @@
 			this.lstGraph.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstGraph.BackColor = System.Drawing.Color.White;
+			this.lstGraph.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstGraph.ForeColor = System.Drawing.Color.Black;
 			this.lstGraph.FormattingEnabled = true;
-			this.lstGraph.Location = new System.Drawing.Point(3, 27);
+			this.lstGraph.Location = new System.Drawing.Point(3, 33);
 			this.lstGraph.Name = "lstGraph";
-			this.lstGraph.Size = new System.Drawing.Size(205, 706);
+			this.lstGraph.Size = new System.Drawing.Size(205, 693);
 			this.lstGraph.TabIndex = 1;
 			this.lstGraph.SelectedIndexChanged += new System.EventHandler(this.lstGraph_SelectedIndexChanged);
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(0, 7);
+			this.label1.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.label1.ForeColor = System.Drawing.Color.Blue;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.label1.Location = new System.Drawing.Point(0, 5);
 			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(87, 13);
+			this.label1.Size = new System.Drawing.Size(124, 21);
 			this.label1.TabIndex = 0;
 			this.label1.Text = "Choose a Graph:";
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.viewPanel);
+			this.skinnedPanel1.Controls.Add(this.lblViews);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Top;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 0);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
+			this.skinnedPanel1.Size = new System.Drawing.Size(983, 33);
+			this.skinnedPanel1.TabIndex = 3;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
 			// viewPanel
 			// 
 			this.viewPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.viewPanel.Location = new System.Drawing.Point(48, 3);
+			this.viewPanel.Location = new System.Drawing.Point(50, 0);
 			this.viewPanel.Name = "viewPanel";
-			this.viewPanel.Size = new System.Drawing.Size(932, 30);
+			this.viewPanel.Size = new System.Drawing.Size(933, 30);
 			this.viewPanel.TabIndex = 2;
 			// 
 			// lblViews
 			// 
 			this.lblViews.AutoSize = true;
-			this.lblViews.Location = new System.Drawing.Point(4, 8);
+			this.lblViews.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblViews.ForeColor = System.Drawing.Color.Black;
+			this.lblViews.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblViews.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblViews.Location = new System.Drawing.Point(3, 9);
 			this.lblViews.Name = "lblViews";
 			this.lblViews.Size = new System.Drawing.Size(38, 13);
 			this.lblViews.TabIndex = 1;
@@ -107,8 +131,10 @@
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.chartContainer.Location = new System.Drawing.Point(3, 39);
 			this.chartContainer.Name = "chartContainer";
+			this.chartContainer.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.chartContainer.Size = new System.Drawing.Size(977, 699);
 			this.chartContainer.TabIndex = 0;
+			this.chartContainer.TabSide = Desktop.Skinning.TabSide.None;
 			// 
 			// ChartHost
 			// 
@@ -120,9 +146,10 @@
 			this.splitContainer1.Panel1.ResumeLayout(false);
 			this.splitContainer1.Panel1.PerformLayout();
 			this.splitContainer1.Panel2.ResumeLayout(false);
-			this.splitContainer1.Panel2.PerformLayout();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
 			this.splitContainer1.ResumeLayout(false);
+			this.skinnedPanel1.ResumeLayout(false);
+			this.skinnedPanel1.PerformLayout();
 			this.ResumeLayout(false);
 
 		}
@@ -130,10 +157,11 @@
 		#endregion
 
 		private System.Windows.Forms.SplitContainer splitContainer1;
-		private System.Windows.Forms.ListBox lstGraph;
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedListBox lstGraph;
+		private Desktop.Skinning.SkinnedLabel label1;
 		private System.Windows.Forms.FlowLayoutPanel viewPanel;
-		private System.Windows.Forms.Label lblViews;
-		private System.Windows.Forms.Panel chartContainer;
+		private Desktop.Skinning.SkinnedLabel lblViews;
+		private Desktop.Skinning.SkinnedPanel chartContainer;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/ChartHost.cs b/editor source/SPNATI Character Editor/Activities/ChartHost.cs
index f88535317e389978c366868700aae2dcea5e9e22..f6b63fc5839575cedaf47d114d7f4ebeff9ff48b 100644
--- a/editor source/SPNATI Character Editor/Activities/ChartHost.cs	
+++ b/editor source/SPNATI Character Editor/Activities/ChartHost.cs	
@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using System.Reflection;
 using System.Windows.Forms;
+using Desktop.Skinning;
 
 namespace SPNATI_Character_Editor.Activities
 {
@@ -104,7 +105,7 @@ namespace SPNATI_Character_Editor.Activities
 			viewPanel.Controls.Clear();
 			for (int i = 0; i < _availableViews.Length; i++)
 			{
-				RadioButton button = new RadioButton();
+				RadioButton button = new SkinnedRadioButton();
 				viewPanel.Controls.Add(button);
 				button.Text = _availableViews[i];
 				if (i == 0)
@@ -165,6 +166,11 @@ namespace SPNATI_Character_Editor.Activities
 				return Order.CompareTo(other.Order);
 			}
 		}
+
+		protected override void OnSkinChanged(Skin skin)
+		{
+			base.OnSkinChanged(skin);
+		}
 	}
 
 	public class ChartRecord : BasicRecord
diff --git a/editor source/SPNATI Character Editor/Activities/CollectibleEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/CollectibleEditor.Designer.cs
index df04cec314f8458d658287be6e2825c4c7346e80..4ff58d209a7ddb259393d880d8b6b3f93eb9ad59 100644
--- a/editor source/SPNATI Character Editor/Activities/CollectibleEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/CollectibleEditor.Designer.cs	
@@ -33,10 +33,13 @@
 			this.toolStrip1 = new System.Windows.Forms.ToolStrip();
 			this.tsAdd = new System.Windows.Forms.ToolStripButton();
 			this.tsRemove = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+			this.tsUp = new System.Windows.Forms.ToolStripButton();
+			this.tsDown = new System.Windows.Forms.ToolStripButton();
 			this.splitContainer2 = new System.Windows.Forms.SplitContainer();
 			this.table = new Desktop.CommonControls.PropertyTable();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
 			this.picPreview = new System.Windows.Forms.PictureBox();
-			this.label1 = new System.Windows.Forms.Label();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
 			this.splitContainer1.Panel1.SuspendLayout();
 			this.splitContainer1.Panel2.SuspendLayout();
@@ -85,7 +88,10 @@
 			this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
 			this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.tsAdd,
-            this.tsRemove});
+            this.tsRemove,
+            this.toolStripSeparator1,
+            this.tsUp,
+            this.tsDown});
 			this.toolStrip1.Location = new System.Drawing.Point(0, 0);
 			this.toolStrip1.Name = "toolStrip1";
 			this.toolStrip1.Size = new System.Drawing.Size(197, 25);
@@ -112,6 +118,31 @@
 			this.tsRemove.Text = "Remove Collectible";
 			this.tsRemove.Click += new System.EventHandler(this.tsRemove_Click);
 			// 
+			// toolStripSeparator1
+			// 
+			this.toolStripSeparator1.Name = "toolStripSeparator1";
+			this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
+			// 
+			// tsUp
+			// 
+			this.tsUp.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsUp.Image = global::SPNATI_Character_Editor.Properties.Resources.UpArrow;
+			this.tsUp.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsUp.Name = "tsUp";
+			this.tsUp.Size = new System.Drawing.Size(23, 22);
+			this.tsUp.Text = "Move Up";
+			this.tsUp.Click += new System.EventHandler(this.tsUp_Click);
+			// 
+			// tsDown
+			// 
+			this.tsDown.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsDown.Image = global::SPNATI_Character_Editor.Properties.Resources.DownArrow;
+			this.tsDown.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsDown.Name = "tsDown";
+			this.tsDown.Size = new System.Drawing.Size(23, 22);
+			this.tsDown.Text = "Move Down";
+			this.tsDown.Click += new System.EventHandler(this.tsDown_Click);
+			// 
 			// splitContainer2
 			// 
 			this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
@@ -154,6 +185,18 @@
 			this.table.UndoManager = null;
 			this.table.UseAutoComplete = false;
 			this.table.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(this.table_PropertyChanged);
+			// 
+			// label1
+			// 
+			this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.label1.Location = new System.Drawing.Point(4, 4);
+			this.label1.Margin = new System.Windows.Forms.Padding(0);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(286, 38);
+			this.label1.TabIndex = 1;
+			this.label1.Text = "Note: To unlock a collectible, associate it with dialogue in the Dialogue tab usi" +
+    "ng the trophy button.";
 			// 
 			// picPreview
 			// 
@@ -167,18 +210,6 @@
 			this.picPreview.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
 			this.picPreview.TabIndex = 0;
 			this.picPreview.TabStop = false;
-			// 
-			// label1
-			// 
-			this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.label1.Location = new System.Drawing.Point(4, 4);
-			this.label1.Margin = new System.Windows.Forms.Padding(0);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(286, 38);
-			this.label1.TabIndex = 1;
-			this.label1.Text = "Note: To unlock a collectible, associate it with dialogue in the Dialogue tab usi" +
-    "ng the trophy button.";
 			// 
 			// CollectibleEditor
 			// 
@@ -213,6 +244,9 @@
 		private System.Windows.Forms.SplitContainer splitContainer2;
 		private Desktop.CommonControls.PropertyTable table;
 		private System.Windows.Forms.PictureBox picPreview;
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
+		private System.Windows.Forms.ToolStripButton tsUp;
+		private System.Windows.Forms.ToolStripButton tsDown;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/CollectibleEditor.cs b/editor source/SPNATI Character Editor/Activities/CollectibleEditor.cs
index 6c8139c6a0c0b2b74ba127741ff5f2b0b78ab577..a566fa503dbc0b8ca40f7ca29b95de14ab14400e 100644
--- a/editor source/SPNATI Character Editor/Activities/CollectibleEditor.cs	
+++ b/editor source/SPNATI Character Editor/Activities/CollectibleEditor.cs	
@@ -2,9 +2,12 @@
 using SPNATI_Character_Editor.Controls;
 using SPNATI_Character_Editor.DataStructures;
 using System;
+using System.Collections;
+using System.Collections.Generic;
 using System.Drawing;
 using System.IO;
 using System.Windows.Forms;
+using Desktop.Skinning;
 
 namespace SPNATI_Character_Editor.Activities
 {
@@ -42,6 +45,26 @@ namespace SPNATI_Character_Editor.Activities
 			PopulateCollectibles();
 		}
 
+		protected override void OnParametersUpdated(params object[] parameters)
+		{
+			if (parameters.Length > 0)
+			{
+				ValidationContext context = parameters[0] as ValidationContext;
+				if (context != null)
+				{
+					for (int i = 0; i < lstCollectibles.Items.Count; i++)
+					{
+						Collectible c = lstCollectibles.Items[i].Tag as Collectible;
+						if (c == context.Collectible)
+						{
+							lstCollectibles.Items[i].Selected = true;
+							break;
+						}
+					}
+				}
+			}
+		}
+
 		private void PopulateCollectibles()
 		{
 			lstCollectibles.Items.Clear();
@@ -49,7 +72,6 @@ namespace SPNATI_Character_Editor.Activities
 			{
 				AddCollectible(c, false);
 			}
-			lstCollectibles.Sort();
 		}
 
 		private void AddCollectible(Collectible c, bool select)
@@ -191,6 +213,73 @@ namespace SPNATI_Character_Editor.Activities
 				picPreview.Image = null;
 			}
 		}
+
+		private void tsUp_Click(object sender, EventArgs e)
+		{
+			Collectible collectible = _selectedItem?.Tag as Collectible;
+			if (collectible == null)
+			{
+				return;
+			}
+			List<Collectible> collectibles = _character.Collectibles.Collectibles;
+
+			int index = collectibles.IndexOf(collectible);
+			if (index == 0)
+			{
+				return;
+			}
+			collectibles.Remove(collectible);
+			collectibles.Insert(index - 1, collectible);
+
+			lstCollectibles.ListViewItemSorter = new CollectibleSorter(collectibles);
+			lstCollectibles.Sort();
+		}
+
+		private void tsDown_Click(object sender, EventArgs e)
+		{
+			Collectible collectible = _selectedItem?.Tag as Collectible;
+			if (collectible == null)
+			{
+				return;
+			}
+			List<Collectible> collectibles = _character.Collectibles.Collectibles;
+
+			int index = collectibles.IndexOf(collectible);
+			if (index == collectibles.Count - 1)
+			{
+				return;
+			}
+			collectibles.Remove(collectible);
+			collectibles.Insert(index + 1, collectible);
+
+			lstCollectibles.ListViewItemSorter = new CollectibleSorter(collectibles);
+			lstCollectibles.Sort();
+		}
+
+		private class CollectibleSorter : IComparer
+		{
+			private List<Collectible> _list;
+			public CollectibleSorter(List<Collectible> list)
+			{
+				_list = list;
+			}
+
+			public int Compare(object x, object y)
+			{
+				ListViewItem i1 = x as ListViewItem;
+				ListViewItem i2 = y as ListViewItem;
+				Collectible c1 = i1.Tag as Collectible;
+				Collectible c2 = i2.Tag as Collectible;
+				return _list.IndexOf(c1).CompareTo(_list.IndexOf(c2));
+			}
+		}
+
+		protected override void OnSkinChanged(Skin skin)
+		{
+			base.OnSkinChanged(skin);
+			lstCollectibles.BackColor = skin.FieldBackColor;
+			lstCollectibles.ForeColor = skin.Surface.ForeColor;
+		}
 	}
 
 	public class CollectibleContext : ICharacterContext
diff --git a/editor source/SPNATI Character Editor/Activities/DataAnalyzer.Designer.cs b/editor source/SPNATI Character Editor/Activities/DataAnalyzer.Designer.cs
index 4469a19fa1c2fcf703573b282bd16035522b89dd..a8b48f869820eeccd0517eba4bc53f89aa959b94 100644
--- a/editor source/SPNATI Character Editor/Activities/DataAnalyzer.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/DataAnalyzer.Designer.cs	
@@ -29,64 +29,79 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			this.cmdLoad = new System.Windows.Forms.Button();
-			this.label1 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
-			this.pnlStart = new System.Windows.Forms.Panel();
-			this.pnlLoad = new System.Windows.Forms.Panel();
-			this.progressBar = new System.Windows.Forms.ProgressBar();
-			this.label3 = new System.Windows.Forms.Label();
-			this.pnlEdit = new System.Windows.Forms.Panel();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle5 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle6 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle8 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle9 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle10 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle11 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle12 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle13 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle14 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle15 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.cmdLoad = new Desktop.Skinning.SkinnedButton();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.pnlStart = new Desktop.Skinning.SkinnedPanel();
+			this.pnlLoad = new Desktop.Skinning.SkinnedPanel();
+			this.progressBar = new Desktop.Skinning.SkinnedProgressBar();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.pnlEdit = new Desktop.Skinning.SkinnedPanel();
 			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
-			this.cmdAdd = new System.Windows.Forms.Button();
-			this.label4 = new System.Windows.Forms.Label();
+			this.cmdAdd = new Desktop.Skinning.SkinnedIcon();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
 			this.tree = new Desktop.CommonControls.DBTreeView();
 			this.splitContainer2 = new System.Windows.Forms.SplitContainer();
-			this.gridCriteria = new System.Windows.Forms.DataGridView();
+			this.gridCriteria = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColIndex = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColData = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColOperator = new System.Windows.Forms.DataGridViewComboBoxColumn();
+			this.ColOperator = new Desktop.Skinning.SkinnedDataGridViewComboBoxColumn();
 			this.ColValue = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColDelete = new System.Windows.Forms.DataGridViewButtonColumn();
-			this.groupBox1 = new System.Windows.Forms.GroupBox();
+			this.ColDelete = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
+			this.groupBox1 = new Desktop.Skinning.SkinnedGroupBox();
 			this.picHelp = new System.Windows.Forms.PictureBox();
-			this.txtCustomExpression = new System.Windows.Forms.TextBox();
-			this.radCustom = new System.Windows.Forms.RadioButton();
-			this.radOr = new System.Windows.Forms.RadioButton();
-			this.radAnd = new System.Windows.Forms.RadioButton();
-			this.grpResults = new System.Windows.Forms.GroupBox();
-			this.lstResults = new System.Windows.Forms.ListBox();
-			this.lblError = new System.Windows.Forms.Label();
-			this.grpExamples = new System.Windows.Forms.GroupBox();
-			this.label9 = new System.Windows.Forms.Label();
-			this.label10 = new System.Windows.Forms.Label();
-			this.gridExample3 = new System.Windows.Forms.DataGridView();
+			this.txtCustomExpression = new Desktop.Skinning.SkinnedTextBox();
+			this.radCustom = new Desktop.Skinning.SkinnedRadioButton();
+			this.radOr = new Desktop.Skinning.SkinnedRadioButton();
+			this.radAnd = new Desktop.Skinning.SkinnedRadioButton();
+			this.grpResults = new Desktop.Skinning.SkinnedGroupBox();
+			this.lstResults = new Desktop.Skinning.SkinnedListBox();
+			this.lblError = new Desktop.Skinning.SkinnedLabel();
+			this.grpExamples = new Desktop.Skinning.SkinnedGroupBox();
+			this.label11 = new Desktop.Skinning.SkinnedLabel();
+			this.label12 = new Desktop.Skinning.SkinnedLabel();
+			this.gridExample4 = new Desktop.Skinning.SkinnedDataGridView();
+			this.dataGridViewTextBoxColumn12 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.dataGridViewTextBoxColumn13 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.dataGridViewTextBoxColumn14 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.dataGridViewTextBoxColumn15 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.label9 = new Desktop.Skinning.SkinnedLabel();
+			this.label10 = new Desktop.Skinning.SkinnedLabel();
+			this.gridExample3 = new Desktop.Skinning.SkinnedDataGridView();
 			this.dataGridViewTextBoxColumn8 = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.dataGridViewTextBoxColumn9 = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.dataGridViewTextBoxColumn10 = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.dataGridViewTextBoxColumn11 = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.label8 = new System.Windows.Forms.Label();
-			this.label7 = new System.Windows.Forms.Label();
-			this.gridExample2 = new System.Windows.Forms.DataGridView();
+			this.label8 = new Desktop.Skinning.SkinnedLabel();
+			this.label7 = new Desktop.Skinning.SkinnedLabel();
+			this.gridExample2 = new Desktop.Skinning.SkinnedDataGridView();
 			this.dataGridViewTextBoxColumn4 = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.dataGridViewTextBoxColumn5 = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.dataGridViewTextBoxColumn6 = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.dataGridViewTextBoxColumn7 = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.label6 = new System.Windows.Forms.Label();
-			this.label5 = new System.Windows.Forms.Label();
-			this.gridExample1 = new System.Windows.Forms.DataGridView();
+			this.label6 = new Desktop.Skinning.SkinnedLabel();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
+			this.gridExample1 = new Desktop.Skinning.SkinnedDataGridView();
 			this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.dataGridViewComboBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
-			this.label11 = new System.Windows.Forms.Label();
-			this.label12 = new System.Windows.Forms.Label();
-			this.gridExample4 = new System.Windows.Forms.DataGridView();
-			this.dataGridViewTextBoxColumn12 = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.dataGridViewTextBoxColumn13 = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.dataGridViewTextBoxColumn14 = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.dataGridViewTextBoxColumn15 = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.pnlStart.SuspendLayout();
 			this.pnlLoad.SuspendLayout();
 			this.pnlEdit.SuspendLayout();
@@ -103,15 +118,18 @@
 			((System.ComponentModel.ISupportInitialize)(this.picHelp)).BeginInit();
 			this.grpResults.SuspendLayout();
 			this.grpExamples.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.gridExample4)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridExample3)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridExample2)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridExample1)).BeginInit();
-			((System.ComponentModel.ISupportInitialize)(this.gridExample4)).BeginInit();
 			this.SuspendLayout();
 			// 
 			// cmdLoad
 			// 
 			this.cmdLoad.Anchor = System.Windows.Forms.AnchorStyles.None;
+			this.cmdLoad.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdLoad.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdLoad.Flat = false;
 			this.cmdLoad.Font = new System.Drawing.Font("Microsoft Sans Serif", 36F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
 			this.cmdLoad.Location = new System.Drawing.Point(293, 67);
 			this.cmdLoad.Name = "cmdLoad";
@@ -125,6 +143,10 @@
 			// 
 			this.label1.Anchor = System.Windows.Forms.AnchorStyles.None;
 			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(206, 24);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(390, 13);
@@ -136,6 +158,10 @@
 			// 
 			this.label2.Anchor = System.Windows.Forms.AnchorStyles.None;
 			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label2.Location = new System.Drawing.Point(189, 44);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(423, 13);
@@ -153,8 +179,10 @@
 			this.pnlStart.Controls.Add(this.cmdLoad);
 			this.pnlStart.Location = new System.Drawing.Point(178, 200);
 			this.pnlStart.Name = "pnlStart";
+			this.pnlStart.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.pnlStart.Size = new System.Drawing.Size(813, 198);
 			this.pnlStart.TabIndex = 3;
+			this.pnlStart.TabSide = Desktop.Skinning.TabSide.None;
 			// 
 			// pnlLoad
 			// 
@@ -163,8 +191,10 @@
 			this.pnlLoad.Controls.Add(this.label3);
 			this.pnlLoad.Location = new System.Drawing.Point(319, 250);
 			this.pnlLoad.Name = "pnlLoad";
+			this.pnlLoad.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.pnlLoad.Size = new System.Drawing.Size(530, 100);
 			this.pnlLoad.TabIndex = 4;
+			this.pnlLoad.TabSide = Desktop.Skinning.TabSide.None;
 			this.pnlLoad.Visible = false;
 			// 
 			// progressBar
@@ -180,10 +210,13 @@
 			// 
 			this.label3.Anchor = System.Windows.Forms.AnchorStyles.None;
 			this.label3.AutoSize = true;
-			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label3.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label3.Location = new System.Drawing.Point(205, 15);
 			this.label3.Name = "label3";
-			this.label3.Size = new System.Drawing.Size(107, 25);
+			this.label3.Size = new System.Drawing.Size(54, 13);
 			this.label3.TabIndex = 0;
 			this.label3.Text = "Loading...";
 			// 
@@ -193,8 +226,10 @@
 			this.pnlEdit.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.pnlEdit.Location = new System.Drawing.Point(0, 0);
 			this.pnlEdit.Name = "pnlEdit";
+			this.pnlEdit.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.pnlEdit.Size = new System.Drawing.Size(1169, 615);
 			this.pnlEdit.TabIndex = 5;
+			this.pnlEdit.TabSide = Desktop.Skinning.TabSide.None;
 			this.pnlEdit.Visible = false;
 			// 
 			// splitContainer1
@@ -219,9 +254,12 @@
 			// cmdAdd
 			// 
 			this.cmdAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdAdd.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdAdd.Enabled = false;
+			this.cmdAdd.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdAdd.Flat = false;
 			this.cmdAdd.Image = global::SPNATI_Character_Editor.Properties.Resources.Add;
-			this.cmdAdd.Location = new System.Drawing.Point(232, 27);
+			this.cmdAdd.Location = new System.Drawing.Point(232, 33);
 			this.cmdAdd.Name = "cmdAdd";
 			this.cmdAdd.Size = new System.Drawing.Size(25, 23);
 			this.cmdAdd.TabIndex = 3;
@@ -232,9 +270,13 @@
 			// label4
 			// 
 			this.label4.AutoSize = true;
+			this.label4.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.label4.ForeColor = System.Drawing.Color.Blue;
+			this.label4.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
 			this.label4.Location = new System.Drawing.Point(3, 9);
 			this.label4.Name = "label4";
-			this.label4.Size = new System.Drawing.Size(62, 13);
+			this.label4.Size = new System.Drawing.Size(88, 21);
 			this.label4.TabIndex = 8;
 			this.label4.Text = "Data Points";
 			// 
@@ -243,9 +285,9 @@
 			this.tree.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.tree.Location = new System.Drawing.Point(6, 27);
+			this.tree.Location = new System.Drawing.Point(6, 33);
 			this.tree.Name = "tree";
-			this.tree.Size = new System.Drawing.Size(224, 585);
+			this.tree.Size = new System.Drawing.Size(224, 579);
 			this.tree.TabIndex = 0;
 			this.tree.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tree_AfterSelect);
 			this.tree.DoubleClick += new System.EventHandler(this.tree_DoubleClick);
@@ -258,7 +300,6 @@
 			// 
 			// splitContainer2.Panel1
 			// 
-			this.splitContainer2.Panel1.Controls.Add(this.gridCriteria);
 			this.splitContainer2.Panel1.Controls.Add(this.groupBox1);
 			this.splitContainer2.Panel1.Controls.Add(this.grpResults);
 			// 
@@ -274,8 +315,21 @@
 			this.gridCriteria.AllowUserToAddRows = false;
 			this.gridCriteria.AllowUserToDeleteRows = false;
 			this.gridCriteria.AllowUserToResizeRows = false;
-			this.gridCriteria.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+			this.gridCriteria.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridCriteria.BackgroundColor = System.Drawing.Color.White;
+			this.gridCriteria.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridCriteria.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridCriteria.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
 			this.gridCriteria.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridCriteria.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColIndex,
@@ -283,12 +337,32 @@
             this.ColOperator,
             this.ColValue,
             this.ColDelete});
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle2.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridCriteria.DefaultCellStyle = dataGridViewCellStyle2;
 			this.gridCriteria.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
-			this.gridCriteria.Location = new System.Drawing.Point(4, 47);
+			this.gridCriteria.EnableHeadersVisualStyles = false;
+			this.gridCriteria.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridCriteria.GridColor = System.Drawing.Color.LightGray;
+			this.gridCriteria.Location = new System.Drawing.Point(6, 49);
 			this.gridCriteria.MultiSelect = false;
 			this.gridCriteria.Name = "gridCriteria";
+			this.gridCriteria.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle3.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridCriteria.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
 			this.gridCriteria.RowHeadersVisible = false;
-			this.gridCriteria.Size = new System.Drawing.Size(545, 148);
+			this.gridCriteria.Size = new System.Drawing.Size(533, 149);
 			this.gridCriteria.TabIndex = 2;
 			this.gridCriteria.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridCriteria_CellContentClick);
 			this.gridCriteria.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.gridCriteria_CellPainting);
@@ -312,8 +386,11 @@
 			// 
 			// ColOperator
 			// 
+			this.ColOperator.AutoComplete = false;
+			this.ColOperator.DisplayMember = null;
 			this.ColOperator.HeaderText = "Operator";
 			this.ColOperator.Name = "ColOperator";
+			this.ColOperator.Sorted = false;
 			this.ColOperator.Width = 80;
 			// 
 			// ColValue
@@ -325,6 +402,8 @@
 			// 
 			// ColDelete
 			// 
+			this.ColDelete.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColDelete.Flat = false;
 			this.ColDelete.HeaderText = "";
 			this.ColDelete.Name = "ColDelete";
 			this.ColDelete.Width = 21;
@@ -333,6 +412,7 @@
 			// 
 			this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.groupBox1.Controls.Add(this.gridCriteria);
 			this.groupBox1.Controls.Add(this.picHelp);
 			this.groupBox1.Controls.Add(this.txtCustomExpression);
 			this.groupBox1.Controls.Add(this.radCustom);
@@ -340,7 +420,7 @@
 			this.groupBox1.Controls.Add(this.radAnd);
 			this.groupBox1.Location = new System.Drawing.Point(3, 3);
 			this.groupBox1.Name = "groupBox1";
-			this.groupBox1.Size = new System.Drawing.Size(546, 51);
+			this.groupBox1.Size = new System.Drawing.Size(546, 204);
 			this.groupBox1.TabIndex = 1;
 			this.groupBox1.TabStop = false;
 			this.groupBox1.Text = "Expression";
@@ -349,7 +429,7 @@
 			// 
 			this.picHelp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
 			this.picHelp.Image = global::SPNATI_Character_Editor.Properties.Resources.Help;
-			this.picHelp.Location = new System.Drawing.Point(523, 20);
+			this.picHelp.Location = new System.Drawing.Point(523, 27);
 			this.picHelp.Name = "picHelp";
 			this.picHelp.Size = new System.Drawing.Size(16, 16);
 			this.picHelp.TabIndex = 4;
@@ -360,7 +440,10 @@
 			// 
 			this.txtCustomExpression.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.txtCustomExpression.Location = new System.Drawing.Point(200, 18);
+			this.txtCustomExpression.BackColor = System.Drawing.Color.White;
+			this.txtCustomExpression.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtCustomExpression.ForeColor = System.Drawing.Color.Black;
+			this.txtCustomExpression.Location = new System.Drawing.Point(200, 25);
 			this.txtCustomExpression.Name = "txtCustomExpression";
 			this.txtCustomExpression.Size = new System.Drawing.Size(317, 20);
 			this.txtCustomExpression.TabIndex = 3;
@@ -369,7 +452,7 @@
 			// radCustom
 			// 
 			this.radCustom.AutoSize = true;
-			this.radCustom.Location = new System.Drawing.Point(131, 19);
+			this.radCustom.Location = new System.Drawing.Point(131, 26);
 			this.radCustom.Name = "radCustom";
 			this.radCustom.Size = new System.Drawing.Size(63, 17);
 			this.radCustom.TabIndex = 2;
@@ -381,7 +464,7 @@
 			// radOr
 			// 
 			this.radOr.AutoSize = true;
-			this.radOr.Location = new System.Drawing.Point(73, 19);
+			this.radOr.Location = new System.Drawing.Point(73, 26);
 			this.radOr.Name = "radOr";
 			this.radOr.Size = new System.Drawing.Size(41, 17);
 			this.radOr.TabIndex = 1;
@@ -393,7 +476,7 @@
 			// radAnd
 			// 
 			this.radAnd.AutoSize = true;
-			this.radAnd.Location = new System.Drawing.Point(6, 19);
+			this.radAnd.Location = new System.Drawing.Point(6, 26);
 			this.radAnd.Name = "radAnd";
 			this.radAnd.Size = new System.Drawing.Size(48, 17);
 			this.radAnd.TabIndex = 0;
@@ -409,9 +492,9 @@
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.grpResults.Controls.Add(this.lstResults);
 			this.grpResults.Controls.Add(this.lblError);
-			this.grpResults.Location = new System.Drawing.Point(3, 201);
+			this.grpResults.Location = new System.Drawing.Point(3, 213);
 			this.grpResults.Name = "grpResults";
-			this.grpResults.Size = new System.Drawing.Size(546, 414);
+			this.grpResults.Size = new System.Drawing.Size(546, 402);
 			this.grpResults.TabIndex = 7;
 			this.grpResults.TabStop = false;
 			this.grpResults.Text = "Results";
@@ -420,10 +503,13 @@
 			// 
 			this.lstResults.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left)));
+			this.lstResults.BackColor = System.Drawing.Color.White;
+			this.lstResults.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstResults.ForeColor = System.Drawing.Color.Black;
 			this.lstResults.FormattingEnabled = true;
-			this.lstResults.Location = new System.Drawing.Point(6, 19);
+			this.lstResults.Location = new System.Drawing.Point(6, 25);
 			this.lstResults.Name = "lstResults";
-			this.lstResults.Size = new System.Drawing.Size(182, 381);
+			this.lstResults.Size = new System.Drawing.Size(182, 368);
 			this.lstResults.TabIndex = 4;
 			this.lstResults.DoubleClick += new System.EventHandler(this.lstResults_DoubleClick);
 			// 
@@ -431,8 +517,11 @@
 			// 
 			this.lblError.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblError.ForeColor = System.Drawing.Color.DarkRed;
-			this.lblError.Location = new System.Drawing.Point(194, 19);
+			this.lblError.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblError.ForeColor = System.Drawing.Color.Red;
+			this.lblError.Highlight = Desktop.Skinning.SkinnedHighlight.Bad;
+			this.lblError.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblError.Location = new System.Drawing.Point(194, 26);
 			this.lblError.Name = "lblError";
 			this.lblError.Size = new System.Drawing.Size(346, 119);
 			this.lblError.TabIndex = 6;
@@ -461,10 +550,126 @@
 			this.grpExamples.TabStop = false;
 			this.grpExamples.Text = "Examples";
 			// 
+			// label11
+			// 
+			this.label11.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.label11.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label11.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label11.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label11.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label11.Location = new System.Drawing.Point(6, 508);
+			this.label11.Name = "label11";
+			this.label11.Size = new System.Drawing.Size(333, 31);
+			this.label11.TabIndex = 18;
+			this.label11.Text = "Match characters that do not have the \"shy\" tag.";
+			// 
+			// label12
+			// 
+			this.label12.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.label12.AutoSize = true;
+			this.label12.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label12.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label12.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label12.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label12.Location = new System.Drawing.Point(257, 435);
+			this.label12.Name = "label12";
+			this.label12.Size = new System.Drawing.Size(39, 13);
+			this.label12.TabIndex = 17;
+			this.label12.Text = "NOT 1";
+			// 
+			// gridExample4
+			// 
+			this.gridExample4.AllowUserToAddRows = false;
+			this.gridExample4.AllowUserToDeleteRows = false;
+			this.gridExample4.AllowUserToResizeRows = false;
+			this.gridExample4.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridExample4.BackgroundColor = System.Drawing.Color.White;
+			this.gridExample4.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridExample4.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle4.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle4.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle4.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle4.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle4.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle4.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridExample4.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle4;
+			this.gridExample4.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+			this.gridExample4.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.dataGridViewTextBoxColumn12,
+            this.dataGridViewTextBoxColumn13,
+            this.dataGridViewTextBoxColumn14,
+            this.dataGridViewTextBoxColumn15});
+			dataGridViewCellStyle5.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle5.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle5.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle5.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle5.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle5.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridExample4.DefaultCellStyle = dataGridViewCellStyle5;
+			this.gridExample4.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
+			this.gridExample4.Enabled = false;
+			this.gridExample4.EnableHeadersVisualStyles = false;
+			this.gridExample4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridExample4.GridColor = System.Drawing.Color.LightGray;
+			this.gridExample4.Location = new System.Drawing.Point(6, 449);
+			this.gridExample4.MultiSelect = false;
+			this.gridExample4.Name = "gridExample4";
+			this.gridExample4.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle6.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle6.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle6.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle6.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle6.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle6.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridExample4.RowHeadersDefaultCellStyle = dataGridViewCellStyle6;
+			this.gridExample4.RowHeadersVisible = false;
+			this.gridExample4.Size = new System.Drawing.Size(333, 56);
+			this.gridExample4.TabIndex = 16;
+			// 
+			// dataGridViewTextBoxColumn12
+			// 
+			this.dataGridViewTextBoxColumn12.HeaderText = "#";
+			this.dataGridViewTextBoxColumn12.Name = "dataGridViewTextBoxColumn12";
+			this.dataGridViewTextBoxColumn12.ReadOnly = true;
+			this.dataGridViewTextBoxColumn12.Width = 21;
+			// 
+			// dataGridViewTextBoxColumn13
+			// 
+			this.dataGridViewTextBoxColumn13.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.dataGridViewTextBoxColumn13.HeaderText = "Property";
+			this.dataGridViewTextBoxColumn13.Name = "dataGridViewTextBoxColumn13";
+			this.dataGridViewTextBoxColumn13.ReadOnly = true;
+			this.dataGridViewTextBoxColumn13.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+			// 
+			// dataGridViewTextBoxColumn14
+			// 
+			this.dataGridViewTextBoxColumn14.HeaderText = "Operator";
+			this.dataGridViewTextBoxColumn14.Name = "dataGridViewTextBoxColumn14";
+			this.dataGridViewTextBoxColumn14.Resizable = System.Windows.Forms.DataGridViewTriState.True;
+			this.dataGridViewTextBoxColumn14.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+			this.dataGridViewTextBoxColumn14.Width = 80;
+			// 
+			// dataGridViewTextBoxColumn15
+			// 
+			this.dataGridViewTextBoxColumn15.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.dataGridViewTextBoxColumn15.HeaderText = "Value";
+			this.dataGridViewTextBoxColumn15.Name = "dataGridViewTextBoxColumn15";
+			this.dataGridViewTextBoxColumn15.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+			// 
 			// label9
 			// 
 			this.label9.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label9.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label9.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label9.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label9.Location = new System.Drawing.Point(6, 404);
 			this.label9.Name = "label9";
 			this.label9.Size = new System.Drawing.Size(333, 31);
@@ -476,7 +681,11 @@
 			// 
 			this.label10.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
 			this.label10.AutoSize = true;
-			this.label10.Location = new System.Drawing.Point(257, 295);
+			this.label10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label10.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label10.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label10.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label10.Location = new System.Drawing.Point(257, 288);
 			this.label10.Name = "label10";
 			this.label10.Size = new System.Drawing.Size(82, 13);
 			this.label10.TabIndex = 14;
@@ -489,19 +698,51 @@
 			this.gridExample3.AllowUserToResizeRows = false;
 			this.gridExample3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridExample3.BackgroundColor = System.Drawing.Color.White;
+			this.gridExample3.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridExample3.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle7.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle7.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle7.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle7.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle7.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle7.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridExample3.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle7;
 			this.gridExample3.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridExample3.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.dataGridViewTextBoxColumn8,
             this.dataGridViewTextBoxColumn9,
             this.dataGridViewTextBoxColumn10,
             this.dataGridViewTextBoxColumn11});
+			dataGridViewCellStyle8.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle8.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle8.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle8.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle8.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle8.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridExample3.DefaultCellStyle = dataGridViewCellStyle8;
 			this.gridExample3.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
 			this.gridExample3.Enabled = false;
-			this.gridExample3.Location = new System.Drawing.Point(6, 309);
+			this.gridExample3.EnableHeadersVisualStyles = false;
+			this.gridExample3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridExample3.GridColor = System.Drawing.Color.LightGray;
+			this.gridExample3.Location = new System.Drawing.Point(6, 304);
 			this.gridExample3.MultiSelect = false;
 			this.gridExample3.Name = "gridExample3";
+			this.gridExample3.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle9.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle9.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle9.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle9.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle9.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle9.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridExample3.RowHeadersDefaultCellStyle = dataGridViewCellStyle9;
 			this.gridExample3.RowHeadersVisible = false;
-			this.gridExample3.Size = new System.Drawing.Size(333, 92);
+			this.gridExample3.Size = new System.Drawing.Size(333, 97);
 			this.gridExample3.TabIndex = 13;
 			// 
 			// dataGridViewTextBoxColumn8
@@ -538,7 +779,11 @@
 			// 
 			this.label8.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.label8.Location = new System.Drawing.Point(6, 264);
+			this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label8.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label8.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label8.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label8.Location = new System.Drawing.Point(6, 247);
 			this.label8.Name = "label8";
 			this.label8.Size = new System.Drawing.Size(333, 31);
 			this.label8.TabIndex = 12;
@@ -548,6 +793,10 @@
 			// 
 			this.label7.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
 			this.label7.AutoSize = true;
+			this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label7.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label7.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label7.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label7.Location = new System.Drawing.Point(309, 156);
 			this.label7.Name = "label7";
 			this.label7.Size = new System.Drawing.Size(30, 13);
@@ -561,19 +810,51 @@
 			this.gridExample2.AllowUserToResizeRows = false;
 			this.gridExample2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridExample2.BackgroundColor = System.Drawing.Color.White;
+			this.gridExample2.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridExample2.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle10.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle10.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle10.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle10.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle10.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle10.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle10.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridExample2.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle10;
 			this.gridExample2.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridExample2.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.dataGridViewTextBoxColumn4,
             this.dataGridViewTextBoxColumn5,
             this.dataGridViewTextBoxColumn6,
             this.dataGridViewTextBoxColumn7});
+			dataGridViewCellStyle11.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle11.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle11.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle11.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle11.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle11.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle11.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridExample2.DefaultCellStyle = dataGridViewCellStyle11;
 			this.gridExample2.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
 			this.gridExample2.Enabled = false;
+			this.gridExample2.EnableHeadersVisualStyles = false;
+			this.gridExample2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridExample2.GridColor = System.Drawing.Color.LightGray;
 			this.gridExample2.Location = new System.Drawing.Point(6, 172);
 			this.gridExample2.MultiSelect = false;
 			this.gridExample2.Name = "gridExample2";
+			this.gridExample2.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle12.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle12.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle12.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle12.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle12.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle12.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle12.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridExample2.RowHeadersDefaultCellStyle = dataGridViewCellStyle12;
 			this.gridExample2.RowHeadersVisible = false;
-			this.gridExample2.Size = new System.Drawing.Size(333, 86);
+			this.gridExample2.Size = new System.Drawing.Size(333, 72);
 			this.gridExample2.TabIndex = 10;
 			// 
 			// dataGridViewTextBoxColumn4
@@ -610,7 +891,11 @@
 			// 
 			this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.label6.Location = new System.Drawing.Point(6, 120);
+			this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label6.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label6.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label6.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label6.Location = new System.Drawing.Point(6, 108);
 			this.label6.Name = "label6";
 			this.label6.Size = new System.Drawing.Size(333, 31);
 			this.label6.TabIndex = 9;
@@ -620,6 +905,10 @@
 			// 
 			this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
 			this.label5.AutoSize = true;
+			this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label5.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label5.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label5.Location = new System.Drawing.Point(309, 15);
 			this.label5.Name = "label5";
 			this.label5.Size = new System.Drawing.Size(30, 13);
@@ -633,19 +922,51 @@
 			this.gridExample1.AllowUserToResizeRows = false;
 			this.gridExample1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridExample1.BackgroundColor = System.Drawing.Color.White;
+			this.gridExample1.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridExample1.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle13.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle13.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle13.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle13.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle13.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle13.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle13.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle13.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridExample1.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle13;
 			this.gridExample1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridExample1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.dataGridViewTextBoxColumn1,
             this.dataGridViewTextBoxColumn2,
             this.dataGridViewComboBoxColumn1,
             this.dataGridViewTextBoxColumn3});
+			dataGridViewCellStyle14.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle14.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle14.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle14.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle14.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle14.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle14.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridExample1.DefaultCellStyle = dataGridViewCellStyle14;
 			this.gridExample1.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
 			this.gridExample1.Enabled = false;
+			this.gridExample1.EnableHeadersVisualStyles = false;
+			this.gridExample1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridExample1.GridColor = System.Drawing.Color.LightGray;
 			this.gridExample1.Location = new System.Drawing.Point(6, 31);
 			this.gridExample1.MultiSelect = false;
 			this.gridExample1.Name = "gridExample1";
+			this.gridExample1.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle15.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle15.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle15.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle15.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle15.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle15.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle15.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridExample1.RowHeadersDefaultCellStyle = dataGridViewCellStyle15;
 			this.gridExample1.RowHeadersVisible = false;
-			this.gridExample1.Size = new System.Drawing.Size(333, 86);
+			this.gridExample1.Size = new System.Drawing.Size(333, 74);
 			this.gridExample1.TabIndex = 7;
 			// 
 			// dataGridViewTextBoxColumn1
@@ -678,78 +999,6 @@
 			this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3";
 			this.dataGridViewTextBoxColumn3.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
 			// 
-			// label11
-			// 
-			this.label11.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.label11.Location = new System.Drawing.Point(6, 544);
-			this.label11.Name = "label11";
-			this.label11.Size = new System.Drawing.Size(333, 31);
-			this.label11.TabIndex = 18;
-			this.label11.Text = "Match characters that do not have the \"shy\" tag.";
-			// 
-			// label12
-			// 
-			this.label12.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.label12.AutoSize = true;
-			this.label12.Location = new System.Drawing.Point(257, 435);
-			this.label12.Name = "label12";
-			this.label12.Size = new System.Drawing.Size(39, 13);
-			this.label12.TabIndex = 17;
-			this.label12.Text = "NOT 1";
-			// 
-			// gridExample4
-			// 
-			this.gridExample4.AllowUserToAddRows = false;
-			this.gridExample4.AllowUserToDeleteRows = false;
-			this.gridExample4.AllowUserToResizeRows = false;
-			this.gridExample4.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.gridExample4.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
-			this.gridExample4.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
-            this.dataGridViewTextBoxColumn12,
-            this.dataGridViewTextBoxColumn13,
-            this.dataGridViewTextBoxColumn14,
-            this.dataGridViewTextBoxColumn15});
-			this.gridExample4.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
-			this.gridExample4.Enabled = false;
-			this.gridExample4.Location = new System.Drawing.Point(6, 449);
-			this.gridExample4.MultiSelect = false;
-			this.gridExample4.Name = "gridExample4";
-			this.gridExample4.RowHeadersVisible = false;
-			this.gridExample4.Size = new System.Drawing.Size(333, 92);
-			this.gridExample4.TabIndex = 16;
-			// 
-			// dataGridViewTextBoxColumn12
-			// 
-			this.dataGridViewTextBoxColumn12.HeaderText = "#";
-			this.dataGridViewTextBoxColumn12.Name = "dataGridViewTextBoxColumn12";
-			this.dataGridViewTextBoxColumn12.ReadOnly = true;
-			this.dataGridViewTextBoxColumn12.Width = 21;
-			// 
-			// dataGridViewTextBoxColumn13
-			// 
-			this.dataGridViewTextBoxColumn13.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
-			this.dataGridViewTextBoxColumn13.HeaderText = "Property";
-			this.dataGridViewTextBoxColumn13.Name = "dataGridViewTextBoxColumn13";
-			this.dataGridViewTextBoxColumn13.ReadOnly = true;
-			this.dataGridViewTextBoxColumn13.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-			// 
-			// dataGridViewTextBoxColumn14
-			// 
-			this.dataGridViewTextBoxColumn14.HeaderText = "Operator";
-			this.dataGridViewTextBoxColumn14.Name = "dataGridViewTextBoxColumn14";
-			this.dataGridViewTextBoxColumn14.Resizable = System.Windows.Forms.DataGridViewTriState.True;
-			this.dataGridViewTextBoxColumn14.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-			this.dataGridViewTextBoxColumn14.Width = 80;
-			// 
-			// dataGridViewTextBoxColumn15
-			// 
-			this.dataGridViewTextBoxColumn15.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
-			this.dataGridViewTextBoxColumn15.HeaderText = "Value";
-			this.dataGridViewTextBoxColumn15.Name = "dataGridViewTextBoxColumn15";
-			this.dataGridViewTextBoxColumn15.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-			// 
 			// DataAnalyzer
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -780,70 +1029,70 @@
 			this.grpResults.ResumeLayout(false);
 			this.grpExamples.ResumeLayout(false);
 			this.grpExamples.PerformLayout();
+			((System.ComponentModel.ISupportInitialize)(this.gridExample4)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridExample3)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridExample2)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridExample1)).EndInit();
-			((System.ComponentModel.ISupportInitialize)(this.gridExample4)).EndInit();
 			this.ResumeLayout(false);
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.Button cmdLoad;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.Panel pnlStart;
-		private System.Windows.Forms.Panel pnlLoad;
-		private System.Windows.Forms.ProgressBar progressBar;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.Panel pnlEdit;
+		private Desktop.Skinning.SkinnedButton cmdLoad;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedPanel pnlStart;
+		private Desktop.Skinning.SkinnedPanel pnlLoad;
+		private Desktop.Skinning.SkinnedProgressBar progressBar;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedPanel pnlEdit;
 		private Desktop.CommonControls.DBTreeView tree;
-		private System.Windows.Forms.GroupBox groupBox1;
-		private System.Windows.Forms.TextBox txtCustomExpression;
-		private System.Windows.Forms.RadioButton radCustom;
-		private System.Windows.Forms.RadioButton radOr;
-		private System.Windows.Forms.RadioButton radAnd;
-		private System.Windows.Forms.DataGridView gridCriteria;
-		private System.Windows.Forms.Button cmdAdd;
+		private Desktop.Skinning.SkinnedGroupBox groupBox1;
+		private Desktop.Skinning.SkinnedTextBox txtCustomExpression;
+		private Desktop.Skinning.SkinnedRadioButton radCustom;
+		private Desktop.Skinning.SkinnedRadioButton radOr;
+		private Desktop.Skinning.SkinnedRadioButton radAnd;
+		private Desktop.Skinning.SkinnedDataGridView gridCriteria;
+		private Desktop.Skinning.SkinnedIcon cmdAdd;
 		private System.Windows.Forms.ToolTip toolTip1;
-		private System.Windows.Forms.ListBox lstResults;
-		private System.Windows.Forms.Label lblError;
-		private System.Windows.Forms.GroupBox grpResults;
-		private System.Windows.Forms.Label label4;
+		private Desktop.Skinning.SkinnedListBox lstResults;
+		private Desktop.Skinning.SkinnedLabel lblError;
+		private Desktop.Skinning.SkinnedGroupBox grpResults;
+		private Desktop.Skinning.SkinnedLabel label4;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColIndex;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColData;
-		private System.Windows.Forms.DataGridViewComboBoxColumn ColOperator;
+		private Desktop.Skinning.SkinnedDataGridViewComboBoxColumn ColOperator;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColValue;
-		private System.Windows.Forms.DataGridViewButtonColumn ColDelete;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColDelete;
 		private System.Windows.Forms.PictureBox picHelp;
-		private System.Windows.Forms.DataGridView gridExample1;
+		private Desktop.Skinning.SkinnedDataGridView gridExample1;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewComboBoxColumn1;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3;
-		private System.Windows.Forms.GroupBox grpExamples;
-		private System.Windows.Forms.Label label6;
-		private System.Windows.Forms.Label label5;
-		private System.Windows.Forms.Label label8;
-		private System.Windows.Forms.Label label7;
-		private System.Windows.Forms.DataGridView gridExample2;
+		private Desktop.Skinning.SkinnedGroupBox grpExamples;
+		private Desktop.Skinning.SkinnedLabel label6;
+		private Desktop.Skinning.SkinnedLabel label5;
+		private Desktop.Skinning.SkinnedLabel label8;
+		private Desktop.Skinning.SkinnedLabel label7;
+		private Desktop.Skinning.SkinnedDataGridView gridExample2;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn4;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn5;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn6;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn7;
 		private System.Windows.Forms.SplitContainer splitContainer1;
 		private System.Windows.Forms.SplitContainer splitContainer2;
-		private System.Windows.Forms.Label label9;
-		private System.Windows.Forms.Label label10;
-		private System.Windows.Forms.DataGridView gridExample3;
+		private Desktop.Skinning.SkinnedLabel label9;
+		private Desktop.Skinning.SkinnedLabel label10;
+		private Desktop.Skinning.SkinnedDataGridView gridExample3;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn8;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn9;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn10;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn11;
-		private System.Windows.Forms.Label label11;
-		private System.Windows.Forms.Label label12;
-		private System.Windows.Forms.DataGridView gridExample4;
+		private Desktop.Skinning.SkinnedLabel label11;
+		private Desktop.Skinning.SkinnedLabel label12;
+		private Desktop.Skinning.SkinnedDataGridView gridExample4;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn12;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn13;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn14;
diff --git a/editor source/SPNATI Character Editor/Activities/DataAnalyzer.cs b/editor source/SPNATI Character Editor/Activities/DataAnalyzer.cs
index 07f10ff7cdf5905aa960d4c6ae016b7f45ee7b52..544f075300018e72c5a4e78d662eec2c60f91ea7 100644
--- a/editor source/SPNATI Character Editor/Activities/DataAnalyzer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/DataAnalyzer.cs	
@@ -1,4 +1,5 @@
 using Desktop;
+using Desktop.Skinning;
 using SPNATI_Character_Editor.Analyzers;
 using System;
 using System.Collections.Generic;
@@ -15,7 +16,7 @@ namespace SPNATI_Character_Editor.Activities
 		public DataAnalyzer()
 		{
 			InitializeComponent();
-
+			ColDelete.Flat = true;
 			ColOperator.ValueType = typeof(string);
 
 			radAnd.Checked = true;
@@ -39,7 +40,7 @@ namespace SPNATI_Character_Editor.Activities
 			gridExample3.Rows.Add(new object[] { "2", "Wardrobe - Layer Count", ">=", "6" });
 			gridExample3.Rows.Add(new object[] { "3", "Targeted Line Count", ">=", "50" });
 
-			gridExample1.Rows.Add(new object[] { "1", "Tags", "==", "shy" });
+			gridExample4.Rows.Add(new object[] { "1", "Tags", "==", "shy" });
 		}
 
 		private void cmdLoad_Click(object sender, System.EventArgs e)
@@ -200,7 +201,7 @@ namespace SPNATI_Character_Editor.Activities
 			row.Cells[nameof(ColIndex)].Value = gridCriteria.Rows.Count;
 			row.Cells[nameof(ColData)].Value = analyzer.FullName;
 
-			DataGridViewComboBoxCell combo = row.Cells[nameof(ColOperator)] as DataGridViewComboBoxCell;
+			SkinnedDataGridViewComboBoxCell combo = row.Cells[nameof(ColOperator)] as SkinnedDataGridViewComboBoxCell;
 			combo.Items.Clear();
 			if (analyzer != null)
 			{
diff --git a/editor source/SPNATI Character Editor/Activities/DataRecovery.Designer.cs b/editor source/SPNATI Character Editor/Activities/DataRecovery.Designer.cs
index 3a10e9e7f2995bbdbcd7b34c1ee0d514391b8786..bc7d07aa7012466d5a5a15275effc76bcf5c4ba2 100644
--- a/editor source/SPNATI Character Editor/Activities/DataRecovery.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/DataRecovery.Designer.cs	
@@ -28,25 +28,31 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.label1 = new System.Windows.Forms.Label();
-			this.lblCharacter = new System.Windows.Forms.Label();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.lblCharacter = new Desktop.Skinning.SkinnedLabel();
 			this.recCharacter = new Desktop.CommonControls.RecordField();
-			this.pnlRecovery = new System.Windows.Forms.Panel();
-			this.cmdRecover = new System.Windows.Forms.Button();
-			this.lstSnapshots = new System.Windows.Forms.ListView();
+			this.pnlRecovery = new Desktop.Skinning.SkinnedPanel();
+			this.cmdRecover = new Desktop.Skinning.SkinnedButton();
+			this.lstSnapshots = new Desktop.Skinning.SkinnedListView();
 			this.Time = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
 			this.colLines = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
 			this.colEndings = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
 			this.colPoses = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
-			this.lblCorrupt = new System.Windows.Forms.Label();
+			this.lblCorrupt = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
 			this.pnlRecovery.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdCancel.Location = new System.Drawing.Point(484, 473);
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(493, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 0;
@@ -57,6 +63,7 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(11, 0);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(155, 13);
@@ -66,7 +73,8 @@
 			// lblCharacter
 			// 
 			this.lblCharacter.AutoSize = true;
-			this.lblCharacter.Location = new System.Drawing.Point(12, 15);
+			this.lblCharacter.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblCharacter.Location = new System.Drawing.Point(12, 38);
 			this.lblCharacter.Name = "lblCharacter";
 			this.lblCharacter.Size = new System.Drawing.Size(56, 13);
 			this.lblCharacter.TabIndex = 2;
@@ -75,11 +83,12 @@
 			// recCharacter
 			// 
 			this.recCharacter.AllowCreate = false;
-			this.recCharacter.Location = new System.Drawing.Point(75, 12);
+			this.recCharacter.Location = new System.Drawing.Point(75, 35);
 			this.recCharacter.Name = "recCharacter";
 			this.recCharacter.PlaceholderText = null;
 			this.recCharacter.Record = null;
 			this.recCharacter.RecordContext = null;
+			this.recCharacter.RecordFilter = null;
 			this.recCharacter.RecordKey = null;
 			this.recCharacter.RecordType = null;
 			this.recCharacter.Size = new System.Drawing.Size(150, 20);
@@ -96,18 +105,23 @@
 			this.pnlRecovery.Controls.Add(this.lstSnapshots);
 			this.pnlRecovery.Controls.Add(this.label1);
 			this.pnlRecovery.Enabled = false;
-			this.pnlRecovery.Location = new System.Drawing.Point(1, 38);
+			this.pnlRecovery.Location = new System.Drawing.Point(1, 61);
+			this.pnlRecovery.Margin = new System.Windows.Forms.Padding(0);
 			this.pnlRecovery.Name = "pnlRecovery";
-			this.pnlRecovery.Size = new System.Drawing.Size(569, 429);
+			this.pnlRecovery.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.pnlRecovery.Size = new System.Drawing.Size(569, 417);
 			this.pnlRecovery.TabIndex = 4;
 			// 
 			// cmdRecover
 			// 
-			this.cmdRecover.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+			this.cmdRecover.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdRecover.Background = Desktop.Skinning.SkinnedBackgroundType.SecondaryDark;
 			this.cmdRecover.Enabled = false;
+			this.cmdRecover.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdRecover.Flat = false;
 			this.cmdRecover.Font = new System.Drawing.Font("Microsoft Sans Serif", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.cmdRecover.Location = new System.Drawing.Point(14, 342);
+			this.cmdRecover.Location = new System.Drawing.Point(14, 330);
 			this.cmdRecover.Name = "cmdRecover";
 			this.cmdRecover.Size = new System.Drawing.Size(544, 80);
 			this.cmdRecover.TabIndex = 2;
@@ -128,7 +142,8 @@
 			this.lstSnapshots.FullRowSelect = true;
 			this.lstSnapshots.Location = new System.Drawing.Point(14, 16);
 			this.lstSnapshots.Name = "lstSnapshots";
-			this.lstSnapshots.Size = new System.Drawing.Size(544, 320);
+			this.lstSnapshots.OwnerDraw = true;
+			this.lstSnapshots.Size = new System.Drawing.Size(544, 308);
 			this.lstSnapshots.TabIndex = 0;
 			this.lstSnapshots.UseCompatibleStateImageBehavior = false;
 			this.lstSnapshots.View = System.Windows.Forms.View.Details;
@@ -158,7 +173,8 @@
 			// 
 			this.lblCorrupt.AutoSize = true;
 			this.lblCorrupt.ForeColor = System.Drawing.Color.Maroon;
-			this.lblCorrupt.Location = new System.Drawing.Point(12, 15);
+			this.lblCorrupt.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblCorrupt.Location = new System.Drawing.Point(12, 38);
 			this.lblCorrupt.Name = "lblCorrupt";
 			this.lblCorrupt.Size = new System.Drawing.Size(484, 13);
 			this.lblCorrupt.TabIndex = 5;
@@ -166,22 +182,34 @@
     "he character\'s data.";
 			this.lblCorrupt.Visible = false;
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 478);
+			this.skinnedPanel1.Margin = new System.Windows.Forms.Padding(0);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(571, 30);
+			this.skinnedPanel1.TabIndex = 6;
+			// 
 			// DataRecovery
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.ClientSize = new System.Drawing.Size(571, 508);
 			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.pnlRecovery);
 			this.Controls.Add(this.recCharacter);
 			this.Controls.Add(this.lblCharacter);
-			this.Controls.Add(this.cmdCancel);
 			this.Controls.Add(this.lblCorrupt);
 			this.Name = "DataRecovery";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
 			this.Text = "Data Recovery";
 			this.pnlRecovery.ResumeLayout(false);
 			this.pnlRecovery.PerformLayout();
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -189,17 +217,18 @@
 
 		#endregion
 
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label lblCharacter;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel lblCharacter;
 		private Desktop.CommonControls.RecordField recCharacter;
-		private System.Windows.Forms.Panel pnlRecovery;
-		private System.Windows.Forms.ListView lstSnapshots;
-		private System.Windows.Forms.Button cmdRecover;
+		private Desktop.Skinning.SkinnedPanel pnlRecovery;
+		private Desktop.Skinning.SkinnedListView lstSnapshots;
+		private Desktop.Skinning.SkinnedButton cmdRecover;
 		private System.Windows.Forms.ColumnHeader Time;
 		private System.Windows.Forms.ColumnHeader colLines;
 		private System.Windows.Forms.ColumnHeader colEndings;
 		private System.Windows.Forms.ColumnHeader colPoses;
-		private System.Windows.Forms.Label lblCorrupt;
+		private Desktop.Skinning.SkinnedLabel lblCorrupt;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Activities/DataRecovery.cs b/editor source/SPNATI Character Editor/Activities/DataRecovery.cs
index 94b63d9da91e5e8bb39cbde7bcc68d3855639c88..c3b18585a5efccbaae70f1f1f65f9b9087fecfc7 100644
--- a/editor source/SPNATI Character Editor/Activities/DataRecovery.cs	
+++ b/editor source/SPNATI Character Editor/Activities/DataRecovery.cs	
@@ -1,5 +1,6 @@
 using Desktop;
 using Desktop.CommonControls;
+using Desktop.Skinning;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -7,7 +8,7 @@ using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Activities
 {
-	public partial class DataRecovery : Form
+	public partial class DataRecovery : SkinnedForm
 	{
 		private Character _character;
 		public Character RecoveredCharacter { get; private set; }
diff --git a/editor source/SPNATI Character Editor/Activities/DialogueEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/DialogueEditor.Designer.cs
index 249fd76eac4ed2a4334a4671fb205fd72e74d444..2ab9dedc6d1fe34e3bebc0477bd25bfb605039a3 100644
--- a/editor source/SPNATI Character Editor/Activities/DialogueEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/DialogueEditor.Designer.cs	
@@ -33,11 +33,11 @@ namespace SPNATI_Character_Editor.Activities
 			this.components = new System.ComponentModel.Container();
 			this.triggerMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
-			this.cmdMakeResponse = new System.Windows.Forms.Button();
-			this.cmdCallOut = new System.Windows.Forms.Button();
-			this.splitDialogue = new System.Windows.Forms.SplitContainer();
-			this.treeDialogue = new SPNATI_Character_Editor.Controls.DialogueTree();
+			this.cmdMakeResponse = new Desktop.Skinning.SkinnedButton();
+			this.cmdCallOut = new Desktop.Skinning.SkinnedButton();
+			this.splitDialogue = new Desktop.Skinning.SkinnedSplitContainer();
 			this.panelCase = new System.Windows.Forms.Panel();
+			this.treeDialogue = new SPNATI_Character_Editor.Controls.DialogueTree();
 			this.caseControl = new SPNATI_Character_Editor.Controls.CaseControl();
 			((System.ComponentModel.ISupportInitialize)(this.splitDialogue)).BeginInit();
 			this.splitDialogue.Panel1.SuspendLayout();
@@ -55,11 +55,14 @@ namespace SPNATI_Character_Editor.Activities
 			// cmdMakeResponse
 			// 
 			this.cmdMakeResponse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdMakeResponse.Location = new System.Drawing.Point(508, 3);
+			this.cmdMakeResponse.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdMakeResponse.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdMakeResponse.Flat = false;
+			this.cmdMakeResponse.Location = new System.Drawing.Point(508, 2);
 			this.cmdMakeResponse.Name = "cmdMakeResponse";
 			this.cmdMakeResponse.Size = new System.Drawing.Size(104, 23);
 			this.cmdMakeResponse.TabIndex = 45;
-			this.cmdMakeResponse.Text = "Respond to This...";
+			this.cmdMakeResponse.Text = "Respond";
 			this.toolTip1.SetToolTip(this.cmdMakeResponse, "Creates a response to this case on another character");
 			this.cmdMakeResponse.UseVisualStyleBackColor = true;
 			this.cmdMakeResponse.Click += new System.EventHandler(this.cmdMakeResponse_Click);
@@ -67,11 +70,14 @@ namespace SPNATI_Character_Editor.Activities
 			// cmdCallOut
 			// 
 			this.cmdCallOut.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdCallOut.Location = new System.Drawing.Point(618, 3);
+			this.cmdCallOut.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCallOut.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCallOut.Flat = false;
+			this.cmdCallOut.Location = new System.Drawing.Point(618, 2);
 			this.cmdCallOut.Name = "cmdCallOut";
 			this.cmdCallOut.Size = new System.Drawing.Size(75, 23);
 			this.cmdCallOut.TabIndex = 44;
-			this.cmdCallOut.Text = "Call Out...";
+			this.cmdCallOut.Text = "Call Out";
 			this.toolTip1.SetToolTip(this.cmdCallOut, "Marks this situation as being \"noteworthy\" so it will appear in other character\'s" +
         " Writing Aids.");
 			this.cmdCallOut.UseVisualStyleBackColor = true;
@@ -91,22 +97,10 @@ namespace SPNATI_Character_Editor.Activities
 			// 
 			this.splitDialogue.Panel2.Controls.Add(this.panelCase);
 			this.splitDialogue.Size = new System.Drawing.Size(973, 671);
+			this.splitDialogue.SplitterColor = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
 			this.splitDialogue.SplitterDistance = 266;
 			this.splitDialogue.TabIndex = 16;
 			// 
-			// treeDialogue
-			// 
-			this.treeDialogue.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.treeDialogue.Location = new System.Drawing.Point(3, 3);
-			this.treeDialogue.Name = "treeDialogue";
-			this.treeDialogue.Size = new System.Drawing.Size(259, 665);
-			this.treeDialogue.TabIndex = 40;
-			this.treeDialogue.SelectedNodeChanging += new System.EventHandler<SPNATI_Character_Editor.Controls.CaseSelectionEventArgs>(this.tree_SelectedNodeChanging);
-			this.treeDialogue.SelectedNodeChanged += new System.EventHandler<SPNATI_Character_Editor.Controls.CaseSelectionEventArgs>(this.tree_SelectedCaseChanged);
-			this.treeDialogue.CreatingCase += new System.EventHandler<SPNATI_Character_Editor.Controls.CaseCreationEventArgs>(this.tree_CreatingCase);
-			// 
 			// panelCase
 			// 
 			this.panelCase.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
@@ -122,10 +116,25 @@ namespace SPNATI_Character_Editor.Activities
 			this.panelCase.Size = new System.Drawing.Size(697, 668);
 			this.panelCase.TabIndex = 28;
 			// 
+			// treeDialogue
+			// 
+			this.treeDialogue.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.treeDialogue.Location = new System.Drawing.Point(0, 0);
+			this.treeDialogue.Margin = new System.Windows.Forms.Padding(0);
+			this.treeDialogue.Name = "treeDialogue";
+			this.treeDialogue.Size = new System.Drawing.Size(266, 665);
+			this.treeDialogue.TabIndex = 40;
+			this.treeDialogue.SelectedNodeChanging += new System.EventHandler<SPNATI_Character_Editor.Controls.CaseSelectionEventArgs>(this.tree_SelectedNodeChanging);
+			this.treeDialogue.SelectedNodeChanged += new System.EventHandler<SPNATI_Character_Editor.Controls.CaseSelectionEventArgs>(this.tree_SelectedCaseChanged);
+			this.treeDialogue.CreatingCase += new System.EventHandler<SPNATI_Character_Editor.Controls.CaseCreationEventArgs>(this.tree_CreatingCase);
+			// 
 			// caseControl
 			// 
 			this.caseControl.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.caseControl.Location = new System.Drawing.Point(0, 0);
+			this.caseControl.Margin = new System.Windows.Forms.Padding(0);
 			this.caseControl.Name = "caseControl";
 			this.caseControl.Size = new System.Drawing.Size(697, 668);
 			this.caseControl.TabIndex = 46;
@@ -148,12 +157,12 @@ namespace SPNATI_Character_Editor.Activities
 
 		#endregion
 
-		private System.Windows.Forms.SplitContainer splitDialogue;
+		private Desktop.Skinning.SkinnedSplitContainer splitDialogue;
 		private System.Windows.Forms.Panel panelCase;
 		private System.Windows.Forms.ContextMenuStrip triggerMenu;
-		private System.Windows.Forms.Button cmdCallOut;
+		private Desktop.Skinning.SkinnedButton cmdCallOut;
 		private System.Windows.Forms.ToolTip toolTip1;
-		private System.Windows.Forms.Button cmdMakeResponse;
+		private Desktop.Skinning.SkinnedButton cmdMakeResponse;
 		private Controls.DialogueTree treeDialogue;
 		private CaseControl caseControl;
 	}
diff --git a/editor source/SPNATI Character Editor/Activities/DialogueEditor.cs b/editor source/SPNATI Character Editor/Activities/DialogueEditor.cs
index 92e6d3424e0969ee16c1b49451a47b105dd7b827..d0ea380fe108f5a18314e1bc3f097a79909874f9 100644
--- a/editor source/SPNATI Character Editor/Activities/DialogueEditor.cs	
+++ b/editor source/SPNATI Character Editor/Activities/DialogueEditor.cs	
@@ -1,6 +1,5 @@
 using Desktop;
 using SPNATI_Character_Editor.Controls;
-using SPNATI_Character_Editor.Forms;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -19,7 +18,6 @@ namespace SPNATI_Character_Editor.Activities
 		private Stage _selectedStage;
 		private Case _selectedCase;
 		private FindReplace _findForm;
-		private bool _populatingCase;
 		private bool _pendingWardrobeChange;
 		private bool _exportOnQuit;
 
@@ -49,7 +47,7 @@ namespace SPNATI_Character_Editor.Activities
 			_character = Record as Character;
 			_editorData = CharacterDatabase.GetEditorData(_character);
 			SetupMessageHandlers();
-			panelCase.Enabled = false;
+			panelCase.Visible = false;
 			caseControl.TextUpdated += GridDialogue_TextUpdated;
 			caseControl.HighlightRow += HighlightRow;
 			caseControl.KeyDown += CaseControl_KeyDown;
@@ -263,7 +261,7 @@ namespace SPNATI_Character_Editor.Activities
 		/// <param name="index"></param>
 		private void HighlightRow(object sender, int index)
 		{
-			if (index == -1 || _populatingCase)
+			if (index == -1)
 				return;
 			string image = caseControl.GetImage(index);
 			CharacterImage img = null;
@@ -291,6 +289,11 @@ namespace SPNATI_Character_Editor.Activities
 		/// <param name="image">Image to display</param>
 		private void DisplayImage(CharacterImage image)
 		{
+			if (_selectedCase != null)
+			{
+				List<string> markers = _selectedCase.GetMarkers();
+				Workspace.SendMessage(WorkspaceMessages.UpdateMarkers, markers);
+			}
 			Workspace.SendMessage(WorkspaceMessages.UpdatePreviewImage, image);
 		}
 
@@ -350,34 +353,22 @@ namespace SPNATI_Character_Editor.Activities
 						MessageBox.Show("Couldn't create a response based on this case's conditions.", "Make Response", MessageBoxButtons.OK, MessageBoxIcon.Error);
 						return;
 					}
-
-					isNew = true;
-					responder.PrepareForEdit();
-				}
-
-				DialogueResponder form = new DialogueResponder(_character, _selectedCase, responder, response);
-				DialogResult result = form.ShowDialog();
-				if (result != DialogResult.Cancel)
-				{
-					if (isNew)
-					{
-						responderData.MarkResponse(_character, _selectedCase, response);
-						responder.Behavior.AddWorkingCase(response);
-					}
-					if (result == DialogResult.Retry)
+					//see if there's a response already matching the conditions of this response and just reuse that if possible
+					existing = responder.Behavior.GetWorkingCases().FirstOrDefault(c => c.MatchesConditions(response));
+					if (existing == null)
 					{
-						Shell.Instance.Launch<Character, DialogueEditor>(responder, response);
+						isNew = true;
 					}
 					else
 					{
-						//if their workspace isn't open, save them now
-						IWorkspace ws = Shell.Instance.GetWorkspace(responder);
-						if (ws == null)
-						{
-							Serialization.ExportCharacter(responder);
-						}
+						isNew = false;
+						response = existing;
 					}
+					responder.PrepareForEdit();
 				}
+
+				ResponseRecord record = new ResponseRecord(_character, _selectedCase, responder, response, isNew);
+				Shell.Instance.LaunchWorkspace(record);
 			}
 		}
 
@@ -403,7 +394,6 @@ namespace SPNATI_Character_Editor.Activities
 			{
 				splitDialogue.Panel2.Visible = true;
 				panelCase.Visible = true;
-				panelCase.Enabled = true;
 				cmdCallOut.Enabled = cmdMakeResponse.Enabled = _selectedCase.GetResponseTag(_character, _character) != null;
 			}
 			else
@@ -412,9 +402,7 @@ namespace SPNATI_Character_Editor.Activities
 				splitDialogue.Panel2.Visible = false;
 			}
 
-			_populatingCase = true;
 			caseControl.SetCase(_selectedStage, _selectedCase);
-			_populatingCase = false;
 		}
 
 		private void tree_CreatingCase(object sender, CaseCreationEventArgs e)
diff --git a/editor source/SPNATI Character Editor/Activities/LineImporter.Designer.cs b/editor source/SPNATI Character Editor/Activities/LineImporter.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3a791bc08e944a14d717e2852f55db19cb52ec83
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/LineImporter.Designer.cs	
@@ -0,0 +1,158 @@
+namespace SPNATI_Character_Editor.Activities
+{
+	partial class LineImporter
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.txtCode = new Desktop.Skinning.SkinnedTextBox();
+			this.cmdImport = new Desktop.Skinning.SkinnedButton();
+			this.grpImports = new Desktop.Skinning.SkinnedGroupBox();
+			this.caseEditor = new SPNATI_Character_Editor.Controls.CaseControl();
+			this.lstCases = new Desktop.Skinning.SkinnedListBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.grpImports.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// label2
+			// 
+			this.label2.AutoSize = true;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label2.Location = new System.Drawing.Point(3, 25);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(35, 13);
+			this.label2.TabIndex = 2;
+			this.label2.Text = "Code:";
+			// 
+			// txtCode
+			// 
+			this.txtCode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtCode.BackColor = System.Drawing.Color.White;
+			this.txtCode.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtCode.ForeColor = System.Drawing.Color.Black;
+			this.txtCode.Location = new System.Drawing.Point(47, 22);
+			this.txtCode.Multiline = true;
+			this.txtCode.Name = "txtCode";
+			this.txtCode.Size = new System.Drawing.Size(1020, 55);
+			this.txtCode.TabIndex = 3;
+			// 
+			// cmdImport
+			// 
+			this.cmdImport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdImport.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdImport.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdImport.Flat = false;
+			this.cmdImport.Location = new System.Drawing.Point(1073, 20);
+			this.cmdImport.Name = "cmdImport";
+			this.cmdImport.Size = new System.Drawing.Size(75, 23);
+			this.cmdImport.TabIndex = 4;
+			this.cmdImport.Text = "Import";
+			this.cmdImport.UseVisualStyleBackColor = true;
+			this.cmdImport.Click += new System.EventHandler(this.cmdImport_Click);
+			// 
+			// grpImports
+			// 
+			this.grpImports.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.grpImports.Controls.Add(this.caseEditor);
+			this.grpImports.Controls.Add(this.lstCases);
+			this.grpImports.Location = new System.Drawing.Point(9, 83);
+			this.grpImports.Name = "grpImports";
+			this.grpImports.Size = new System.Drawing.Size(1145, 594);
+			this.grpImports.TabIndex = 6;
+			this.grpImports.TabStop = false;
+			this.grpImports.Text = "Imported Cases";
+			// 
+			// caseEditor
+			// 
+			this.caseEditor.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.caseEditor.Enabled = false;
+			this.caseEditor.Location = new System.Drawing.Point(196, 19);
+			this.caseEditor.Name = "caseEditor";
+			this.caseEditor.Size = new System.Drawing.Size(943, 569);
+			this.caseEditor.TabIndex = 1;
+			this.caseEditor.Visible = false;
+			this.caseEditor.HighlightRow += new System.EventHandler<int>(this.caseEditor_HighlightRow);
+			// 
+			// lstCases
+			// 
+			this.lstCases.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+			this.lstCases.BackColor = System.Drawing.Color.White;
+			this.lstCases.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstCases.ForeColor = System.Drawing.Color.Black;
+			this.lstCases.FormattingEnabled = true;
+			this.lstCases.Location = new System.Drawing.Point(11, 24);
+			this.lstCases.Name = "lstCases";
+			this.lstCases.Size = new System.Drawing.Size(179, 563);
+			this.lstCases.TabIndex = 0;
+			this.lstCases.SelectedIndexChanged += new System.EventHandler(this.lstCases_SelectedIndexChanged);
+			// 
+			// label1
+			// 
+			this.label1.AutoSize = true;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(2, 4);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(570, 13);
+			this.label1.TabIndex = 7;
+			this.label1.Text = "Paste a code from the game\'s Export Dev Mode Edits popup and click Import to load" +
+    " those lines into the character\'s file.";
+			// 
+			// LineImporter
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.label1);
+			this.Controls.Add(this.cmdImport);
+			this.Controls.Add(this.label2);
+			this.Controls.Add(this.grpImports);
+			this.Controls.Add(this.txtCode);
+			this.Name = "LineImporter";
+			this.Size = new System.Drawing.Size(1157, 680);
+			this.grpImports.ResumeLayout(false);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedTextBox txtCode;
+		private Desktop.Skinning.SkinnedGroupBox grpImports;
+		private Desktop.Skinning.SkinnedListBox lstCases;
+		private Controls.CaseControl caseEditor;
+		private Desktop.Skinning.SkinnedButton cmdImport;
+		private Desktop.Skinning.SkinnedLabel label1;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Activities/LineImporter.cs b/editor source/SPNATI Character Editor/Activities/LineImporter.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fdd657e33b47617d658c4c21360434994e946940
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/LineImporter.cs	
@@ -0,0 +1,142 @@
+using Desktop;
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Activities
+{
+	[Activity(typeof(Character), 315)]
+	public partial class LineImporter : Activity
+	{
+		private int _collapsedHeight;
+		private Character _character;
+
+		public LineImporter()
+		{
+			InitializeComponent();
+			_collapsedHeight = txtCode.Height;
+		}
+
+		public override string Caption
+		{
+			get { return "Line Importer"; }
+		}
+
+		protected override void OnInitialize()
+		{
+			_character = Record as Character;
+			ExpandCode(true);
+		}
+
+		public override void Save()
+		{
+			caseEditor.Save();
+		}
+
+		private void ExpandCode(bool expanded)
+		{
+			if (expanded)
+			{
+				txtCode.Height = Height - (Padding.Bottom + txtCode.Top);
+				grpImports.Visible = false;
+			}
+			else
+			{
+				txtCode.Height = _collapsedHeight;
+				grpImports.Visible = true;
+				grpImports.Top = txtCode.Bottom + txtCode.Margin.Bottom;
+				grpImports.Height = Height - (Padding.Bottom + grpImports.Top);
+			}
+		}
+
+		private void Import()
+		{
+			string code = txtCode.Text;
+			ExpandCode(false);
+			if (_character == null || string.IsNullOrEmpty(code)) { return; }
+			lstCases.Items.Clear();
+
+			List<ImportEdit> edits = new List<ImportEdit>();
+			string json = code;
+			try
+			{
+				edits = Json.Deserialize<List<ImportEdit>>(json);
+			}
+			catch (Exception ex)
+			{
+				MessageBox.Show(ex.Message, "Failed to Import", MessageBoxButtons.OK, MessageBoxIcon.Error);
+				return;
+			}
+			List<Case> imports = new List<Case>();
+			List<string> errors = new List<string>();
+			for (int i = 0; i < edits.Count; i++)
+			{
+				ImportEdit edit = edits[i];
+				try
+				{
+					Case importedCase = edit.CreateCase(_character);
+					if (importedCase != null)
+					{
+						lstCases.Items.Add(importedCase);
+						imports.Add(importedCase);
+					}
+				}
+				catch (ImportLinesException ex)
+				{
+					errors.Add($"Error importing edit {i + 1}: {ex.Message}.");
+				}
+			}
+			Shell.Instance.SetStatus($"Imported {imports.Count} case(s) with {errors.Count} error(s).");
+			if (errors.Count > 0)
+			{
+				MessageBox.Show(string.Join("\r\n", errors), "Import Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
+			}
+
+			if (lstCases.Items.Count > 0)
+			{
+				lstCases.SelectedIndex = 0;
+			}
+		}
+
+		private void lstCases_SelectedIndexChanged(object sender, System.EventArgs e)
+		{
+			Case selectedCase = lstCases.SelectedItem as Case;
+			caseEditor.SetCharacter(_character);
+			if (selectedCase == null)
+			{
+				caseEditor.SetCase(null, null);
+				caseEditor.Enabled = false;
+				caseEditor.Visible = false;
+				return;
+			}
+			else
+			{
+				caseEditor.Enabled = true;
+				caseEditor.Visible = true;
+				caseEditor.SetCase(new Stage(selectedCase.Stages[0]), selectedCase);
+			}
+		}
+
+		private void caseEditor_HighlightRow(object sender, int index)
+		{
+			if (_character == null) { return; }
+			string image = caseEditor.GetImage(index);
+			ImageLibrary imageLibrary = ImageLibrary.Get(_character);
+			CharacterImage img = null;
+			img = imageLibrary.Find(image);
+			if (img == null)
+			{
+				Case workingCase = caseEditor.GetCase();
+				int stage = workingCase.Stages[0];
+				image = DialogueLine.GetStageImage(stage, DialogueLine.GetDefaultImage(image));
+				img = imageLibrary.Find(image);
+			}
+			Workspace.SendMessage(WorkspaceMessages.UpdatePreviewImage, img);
+		}
+
+		private void cmdImport_Click(object sender, System.EventArgs e)
+		{
+			Import();
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleCountControl.resx b/editor source/SPNATI Character Editor/Activities/LineImporter.resx
similarity index 100%
rename from editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleCountControl.resx
rename to editor source/SPNATI Character Editor/Activities/LineImporter.resx
diff --git a/editor source/SPNATI Character Editor/Activities/Loader.Designer.cs b/editor source/SPNATI Character Editor/Activities/Loader.Designer.cs
index 1c79adfd5fca12a45202d49a5003f48f6443c6c4..b59724882cd2780c60ea9d7e5c0dd9c9e95933fa 100644
--- a/editor source/SPNATI Character Editor/Activities/Loader.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/Loader.Designer.cs	
@@ -28,8 +28,8 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.progressBar = new System.Windows.Forms.ProgressBar();
-			this.lblProgress = new System.Windows.Forms.Label();
+			this.progressBar = new Desktop.Skinning.SkinnedProgressBar();
+			this.lblProgress = new Desktop.Skinning.SkinnedLabel();
 			this.SuspendLayout();
 			// 
 			// progressBar
@@ -44,6 +44,7 @@
 			// 
 			this.lblProgress.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
 			this.lblProgress.Font = new System.Drawing.Font("Microsoft Sans Serif", 16F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.lblProgress.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
 			this.lblProgress.Location = new System.Drawing.Point(3, 220);
 			this.lblProgress.Name = "lblProgress";
 			this.lblProgress.Size = new System.Drawing.Size(856, 50);
@@ -65,7 +66,7 @@
 
 		#endregion
 
-		private System.Windows.Forms.ProgressBar progressBar;
-		private System.Windows.Forms.Label lblProgress;
+		private Desktop.Skinning.SkinnedProgressBar progressBar;
+		private Desktop.Skinning.SkinnedLabel lblProgress;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/Loader.cs b/editor source/SPNATI Character Editor/Activities/Loader.cs
index 0207bdb08972fe85d7eacb215ee198e031bf97ba..7413253ee3d40c08b0074787ea634563d536442a 100644
--- a/editor source/SPNATI Character Editor/Activities/Loader.cs	
+++ b/editor source/SPNATI Character Editor/Activities/Loader.cs	
@@ -5,6 +5,7 @@ using System.IO;
 using System.Threading.Tasks;
 using System.Linq;
 using SPNATI_Character_Editor.Forms;
+using SPNATI_Character_Editor.Providers;
 
 namespace SPNATI_Character_Editor.Activities
 {
@@ -27,6 +28,7 @@ namespace SPNATI_Character_Editor.Activities
 			await LoadChunk("Tags", 0, () => TagDatabase.Load());
 			await LoadChunk("Variables", 1, () => VariableDatabase.Load());
 			await LoadChunk("Default Dialogue", 1, () => DialogueDatabase.Load());
+			await LoadChunk("Recipes", 1, () => RecipeProvider.Load());
 
 			string lastCharacter = Config.GetString(Settings.LastCharacter);
 
@@ -112,6 +114,11 @@ namespace SPNATI_Character_Editor.Activities
 								}
 							}
 							TagDatabase.AddTag(character.DisplayName, false);
+							for (int l = 0; l < character.Layers; l++)
+							{
+								Clothing layer = character.GetClothing(l);
+								ClothingDatabase.AddClothing(layer);
+							}
 						}
 						else
 						{
@@ -153,6 +160,12 @@ namespace SPNATI_Character_Editor.Activities
 				});
 			}
 
+			//add the default skin
+			Costume defaultCostume = new Costume() { Key = "default" };
+			defaultCostume.Folders.Add(new StageSpecificValue(0, "default"));
+			defaultCostume.Labels.Add(new StageSpecificValue(0, "Default Outfit"));
+			CharacterDatabase.AddSkin(defaultCostume);
+
 			progressBar.Visible = false;
 			//display What's New form if this is a new version
 			if (Config.GetString(Settings.LastVersionRun) != Config.Version)
diff --git a/editor source/SPNATI Character Editor/Activities/MarkerReport.Designer.cs b/editor source/SPNATI Character Editor/Activities/MarkerReport.Designer.cs
index 55e43a8de08ab0069eeeae3f7e52b4902bf837ac..1eb765d4820705120e1ff914c945b1de3945a678 100644
--- a/editor source/SPNATI Character Editor/Activities/MarkerReport.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/MarkerReport.Designer.cs	
@@ -28,14 +28,14 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.lstCharacters = new System.Windows.Forms.ListBox();
-			this.label2 = new System.Windows.Forms.Label();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.lstCharacters = new Desktop.Skinning.SkinnedListBox();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
 			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
 			this.splitContainer2 = new System.Windows.Forms.SplitContainer();
 			this.gridMarker = new SPNATI_Character_Editor.Controls.MarkerGrid();
-			this.label3 = new System.Windows.Forms.Label();
-			this.lstLines = new System.Windows.Forms.ListBox();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.lstLines = new Desktop.Skinning.SkinnedListBox();
 			this.picPortrait = new SPNATI_Character_Editor.Controls.CharacterImageBox();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
 			this.splitContainer1.Panel1.SuspendLayout();
@@ -50,9 +50,13 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(3, 0);
+			this.label1.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.label1.ForeColor = System.Drawing.Color.Blue;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Heading;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.label1.Location = new System.Drawing.Point(3, 6);
 			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(58, 13);
+			this.label1.Size = new System.Drawing.Size(84, 21);
 			this.label1.TabIndex = 2;
 			this.label1.Text = "Characters";
 			// 
@@ -61,19 +65,26 @@
 			this.lstCharacters.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstCharacters.BackColor = System.Drawing.Color.White;
+			this.lstCharacters.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstCharacters.ForeColor = System.Drawing.Color.Black;
 			this.lstCharacters.FormattingEnabled = true;
-			this.lstCharacters.Location = new System.Drawing.Point(6, 16);
+			this.lstCharacters.Location = new System.Drawing.Point(6, 29);
 			this.lstCharacters.Name = "lstCharacters";
-			this.lstCharacters.Size = new System.Drawing.Size(237, 641);
+			this.lstCharacters.Size = new System.Drawing.Size(237, 628);
 			this.lstCharacters.TabIndex = 0;
 			this.lstCharacters.SelectedIndexChanged += new System.EventHandler(this.lstCharacters_SelectedIndexChanged);
 			// 
 			// label2
 			// 
 			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.label2.ForeColor = System.Drawing.Color.Blue;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Heading;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
 			this.label2.Location = new System.Drawing.Point(3, 6);
 			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(45, 13);
+			this.label2.Size = new System.Drawing.Size(67, 21);
 			this.label2.TabIndex = 3;
 			this.label2.Text = "Markers";
 			// 
@@ -122,19 +133,23 @@
 			this.gridMarker.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.gridMarker.Location = new System.Drawing.Point(3, 22);
+			this.gridMarker.Location = new System.Drawing.Point(3, 30);
 			this.gridMarker.Name = "gridMarker";
 			this.gridMarker.ReadOnly = true;
-			this.gridMarker.Size = new System.Drawing.Size(839, 311);
+			this.gridMarker.Size = new System.Drawing.Size(839, 303);
 			this.gridMarker.TabIndex = 4;
 			this.gridMarker.SelectionChanged += new System.EventHandler<SPNATI_Character_Editor.Marker>(this.gridMarker_SelectionChanged);
 			// 
 			// label3
 			// 
 			this.label3.AutoSize = true;
+			this.label3.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.label3.ForeColor = System.Drawing.Color.Blue;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Heading;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
 			this.label3.Location = new System.Drawing.Point(4, 3);
 			this.label3.Name = "label3";
-			this.label3.Size = new System.Drawing.Size(123, 13);
+			this.label3.Size = new System.Drawing.Size(181, 21);
 			this.label3.TabIndex = 2;
 			this.label3.Text = "Lines that set the marker";
 			// 
@@ -143,9 +158,12 @@
 			this.lstLines.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstLines.BackColor = System.Drawing.Color.White;
+			this.lstLines.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstLines.ForeColor = System.Drawing.Color.Black;
 			this.lstLines.FormattingEnabled = true;
 			this.lstLines.HorizontalScrollbar = true;
-			this.lstLines.Location = new System.Drawing.Point(3, 19);
+			this.lstLines.Location = new System.Drawing.Point(3, 26);
 			this.lstLines.Name = "lstLines";
 			this.lstLines.Size = new System.Drawing.Size(667, 290);
 			this.lstLines.TabIndex = 1;
@@ -157,6 +175,7 @@
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.picPortrait.Location = new System.Drawing.Point(676, 3);
 			this.picPortrait.Name = "picPortrait";
+			this.picPortrait.ShowTextBox = false;
 			this.picPortrait.Size = new System.Drawing.Size(166, 319);
 			this.picPortrait.TabIndex = 0;
 			this.picPortrait.TabStop = false;
@@ -185,14 +204,14 @@
 
 		#endregion
 
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.ListBox lstCharacters;
-		private System.Windows.Forms.Label label2;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedListBox lstCharacters;
+		private Desktop.Skinning.SkinnedLabel label2;
 		private System.Windows.Forms.SplitContainer splitContainer1;
 		private System.Windows.Forms.SplitContainer splitContainer2;
 		private Controls.MarkerGrid gridMarker;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.ListBox lstLines;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedListBox lstLines;
 		private SPNATI_Character_Editor.Controls.CharacterImageBox picPortrait;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/MarkerReport.cs b/editor source/SPNATI Character Editor/Activities/MarkerReport.cs
index 958e32a883d5f31b4602e3d86c5ebe0a6d0c2ff5..c92423eacaa621e382ec68dc1c30319eb91b351e 100644
--- a/editor source/SPNATI Character Editor/Activities/MarkerReport.cs	
+++ b/editor source/SPNATI Character Editor/Activities/MarkerReport.cs	
@@ -44,6 +44,7 @@ namespace SPNATI_Character_Editor.Activities
 		private void lstLines_SelectedIndexChanged(object sender, System.EventArgs e)
 		{
 			MarkerItem marker = lstLines.SelectedItem as MarkerItem;
+			if (marker == null) { return; }
 			DialogueLine line = marker.Case.Lines[0];
 			int stage = marker.Case.Stages[0];
 			CharacterImage image = _imageLibrary.Find(string.Format("{0}-{1}", stage, line.Image));
diff --git a/editor source/SPNATI Character Editor/Activities/MetadataEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/MetadataEditor.Designer.cs
index 81d07aa60a61dfb7d28608ba51a2e11fbfffa50c..2b2c5594e63a58261777361f0b32b4cac8694cd1 100644
--- a/editor source/SPNATI Character Editor/Activities/MetadataEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/MetadataEditor.Designer.cs	
@@ -31,45 +31,54 @@
 			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
 			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
 			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
-			this.txtLabel = new System.Windows.Forms.TextBox();
-			this.label1 = new System.Windows.Forms.Label();
-			this.lblUnlisted = new System.Windows.Forms.Label();
-			this.lblTesting = new System.Windows.Forms.Label();
-			this.lblIncomplete = new System.Windows.Forms.Label();
-			this.lblOffline = new System.Windows.Forms.Label();
-			this.gridAI = new System.Windows.Forms.DataGridView();
-			this.label7 = new System.Windows.Forms.Label();
-			this.label22 = new System.Windows.Forms.Label();
-			this.txtDescription = new System.Windows.Forms.TextBox();
-			this.cboDefaultPic = new System.Windows.Forms.ComboBox();
-			this.label24 = new System.Windows.Forms.Label();
-			this.txtHeight = new System.Windows.Forms.TextBox();
-			this.txtLastName = new System.Windows.Forms.TextBox();
-			this.label23 = new System.Windows.Forms.Label();
-			this.label3 = new System.Windows.Forms.Label();
-			this.txtFirstName = new System.Windows.Forms.TextBox();
-			this.label2 = new System.Windows.Forms.Label();
-			this.label19 = new System.Windows.Forms.Label();
-			this.txtArtist = new System.Windows.Forms.TextBox();
-			this.label18 = new System.Windows.Forms.Label();
-			this.txtWriter = new System.Windows.Forms.TextBox();
-			this.label17 = new System.Windows.Forms.Label();
-			this.txtSource = new System.Windows.Forms.TextBox();
-			this.valRounds = new System.Windows.Forms.NumericUpDown();
-			this.label12 = new System.Windows.Forms.Label();
-			this.cboSize = new System.Windows.Forms.ComboBox();
-			this.label10 = new System.Windows.Forms.Label();
-			this.label11 = new System.Windows.Forms.Label();
-			this.cboGender = new System.Windows.Forms.ComboBox();
+			this.txtLabel = new Desktop.Skinning.SkinnedTextBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.lblUnlisted = new Desktop.Skinning.SkinnedLabel();
+			this.lblTesting = new Desktop.Skinning.SkinnedLabel();
+			this.lblIncomplete = new Desktop.Skinning.SkinnedLabel();
+			this.lblOffline = new Desktop.Skinning.SkinnedLabel();
+			this.gridAI = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColAIStage = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColDifficulty = new System.Windows.Forms.DataGridViewComboBoxColumn();
+			this.ColDifficulty = new Desktop.Skinning.SkinnedDataGridViewComboBoxColumn();
+			this.label7 = new Desktop.Skinning.SkinnedLabel();
+			this.label22 = new Desktop.Skinning.SkinnedLabel();
+			this.txtDescription = new Desktop.Skinning.SkinnedTextBox();
+			this.cboDefaultPic = new Desktop.Skinning.SkinnedComboBox();
+			this.label24 = new Desktop.Skinning.SkinnedLabel();
+			this.txtHeight = new Desktop.Skinning.SkinnedTextBox();
+			this.txtLastName = new Desktop.Skinning.SkinnedTextBox();
+			this.label23 = new Desktop.Skinning.SkinnedLabel();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.txtFirstName = new Desktop.Skinning.SkinnedTextBox();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.label19 = new Desktop.Skinning.SkinnedLabel();
+			this.txtArtist = new Desktop.Skinning.SkinnedTextBox();
+			this.label18 = new Desktop.Skinning.SkinnedLabel();
+			this.txtWriter = new Desktop.Skinning.SkinnedTextBox();
+			this.label17 = new Desktop.Skinning.SkinnedLabel();
+			this.txtSource = new Desktop.Skinning.SkinnedTextBox();
+			this.valRounds = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.label12 = new Desktop.Skinning.SkinnedLabel();
+			this.cboSize = new Desktop.Skinning.SkinnedComboBox();
+			this.label10 = new Desktop.Skinning.SkinnedLabel();
+			this.lblSize = new Desktop.Skinning.SkinnedLabel();
+			this.cboGender = new Desktop.Skinning.SkinnedComboBox();
+			this.skinnedGroupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.skinnedGroupBox2 = new Desktop.Skinning.SkinnedGroupBox();
+			this.skinnedGroupBox3 = new Desktop.Skinning.SkinnedGroupBox();
 			((System.ComponentModel.ISupportInitialize)(this.gridAI)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.valRounds)).BeginInit();
+			this.skinnedGroupBox1.SuspendLayout();
+			this.skinnedGroupBox2.SuspendLayout();
+			this.skinnedGroupBox3.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// txtLabel
 			// 
-			this.txtLabel.Location = new System.Drawing.Point(72, 3);
+			this.txtLabel.BackColor = System.Drawing.Color.White;
+			this.txtLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtLabel.ForeColor = System.Drawing.Color.Black;
+			this.txtLabel.Location = new System.Drawing.Point(75, 5);
 			this.txtLabel.Name = "txtLabel";
 			this.txtLabel.Size = new System.Drawing.Size(100, 20);
 			this.txtLabel.TabIndex = 5;
@@ -77,7 +86,11 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(3, 6);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label1.Location = new System.Drawing.Point(3, 8);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(36, 13);
 			this.label1.TabIndex = 4;
@@ -86,8 +99,11 @@
 			// lblUnlisted
 			// 
 			this.lblUnlisted.AutoSize = true;
-			this.lblUnlisted.ForeColor = System.Drawing.Color.DarkOrange;
-			this.lblUnlisted.Location = new System.Drawing.Point(178, 6);
+			this.lblUnlisted.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblUnlisted.ForeColor = System.Drawing.Color.Black;
+			this.lblUnlisted.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblUnlisted.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblUnlisted.Location = new System.Drawing.Point(181, 8);
 			this.lblUnlisted.Name = "lblUnlisted";
 			this.lblUnlisted.Size = new System.Drawing.Size(130, 13);
 			this.lblUnlisted.TabIndex = 22;
@@ -96,8 +112,11 @@
 			// lblTesting
 			// 
 			this.lblTesting.AutoSize = true;
-			this.lblTesting.ForeColor = System.Drawing.Color.Olive;
-			this.lblTesting.Location = new System.Drawing.Point(178, 6);
+			this.lblTesting.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblTesting.ForeColor = System.Drawing.Color.Black;
+			this.lblTesting.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblTesting.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblTesting.Location = new System.Drawing.Point(181, 8);
 			this.lblTesting.Name = "lblTesting";
 			this.lblTesting.Size = new System.Drawing.Size(104, 13);
 			this.lblTesting.TabIndex = 21;
@@ -106,8 +125,11 @@
 			// lblIncomplete
 			// 
 			this.lblIncomplete.AutoSize = true;
-			this.lblIncomplete.ForeColor = System.Drawing.Color.Maroon;
-			this.lblIncomplete.Location = new System.Drawing.Point(178, 6);
+			this.lblIncomplete.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblIncomplete.ForeColor = System.Drawing.Color.Black;
+			this.lblIncomplete.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblIncomplete.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblIncomplete.Location = new System.Drawing.Point(181, 8);
 			this.lblIncomplete.Name = "lblIncomplete";
 			this.lblIncomplete.Size = new System.Drawing.Size(145, 13);
 			this.lblIncomplete.TabIndex = 20;
@@ -116,8 +138,11 @@
 			// lblOffline
 			// 
 			this.lblOffline.AutoSize = true;
-			this.lblOffline.ForeColor = System.Drawing.SystemColors.Highlight;
-			this.lblOffline.Location = new System.Drawing.Point(178, 6);
+			this.lblOffline.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblOffline.ForeColor = System.Drawing.Color.Black;
+			this.lblOffline.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblOffline.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblOffline.Location = new System.Drawing.Point(181, 8);
 			this.lblOffline.Name = "lblOffline";
 			this.lblOffline.Size = new System.Drawing.Size(120, 13);
 			this.lblOffline.TabIndex = 19;
@@ -127,6 +152,9 @@
 			// 
 			this.gridAI.AllowUserToResizeColumns = false;
 			this.gridAI.AllowUserToResizeRows = false;
+			this.gridAI.BackgroundColor = System.Drawing.Color.White;
+			this.gridAI.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridAI.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
 			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
 			dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control;
 			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
@@ -148,8 +176,12 @@
 			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
 			this.gridAI.DefaultCellStyle = dataGridViewCellStyle2;
 			this.gridAI.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
-			this.gridAI.Location = new System.Drawing.Point(363, 161);
+			this.gridAI.EnableHeadersVisualStyles = false;
+			this.gridAI.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridAI.GridColor = System.Drawing.Color.FromArgb(((int)(((byte)(142)))), ((int)(((byte)(153)))), ((int)(((byte)(243)))));
+			this.gridAI.Location = new System.Drawing.Point(107, 27);
 			this.gridAI.Name = "gridAI";
+			this.gridAI.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
 			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
 			dataGridViewCellStyle3.BackColor = System.Drawing.SystemColors.Control;
 			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
@@ -160,12 +192,34 @@
 			this.gridAI.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
 			this.gridAI.RowHeadersVisible = false;
 			this.gridAI.Size = new System.Drawing.Size(212, 101);
-			this.gridAI.TabIndex = 107;
+			this.gridAI.TabIndex = 16;
+			// 
+			// ColAIStage
+			// 
+			this.ColAIStage.HeaderText = "Stage";
+			this.ColAIStage.MinimumWidth = 50;
+			this.ColAIStage.Name = "ColAIStage";
+			this.ColAIStage.Width = 50;
+			// 
+			// ColDifficulty
+			// 
+			this.ColDifficulty.AutoComplete = false;
+			this.ColDifficulty.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.ColDifficulty.DisplayMember = null;
+			this.ColDifficulty.HeaderText = "Intelligence";
+			this.ColDifficulty.Name = "ColDifficulty";
+			this.ColDifficulty.Resizable = System.Windows.Forms.DataGridViewTriState.True;
+			this.ColDifficulty.Sorted = false;
+			this.ColDifficulty.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
 			// 
 			// label7
 			// 
 			this.label7.AutoSize = true;
-			this.label7.Location = new System.Drawing.Point(293, 165);
+			this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label7.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label7.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label7.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label7.Location = new System.Drawing.Point(6, 31);
 			this.label7.Name = "label7";
 			this.label7.Size = new System.Drawing.Size(64, 13);
 			this.label7.TabIndex = 106;
@@ -174,7 +228,11 @@
 			// label22
 			// 
 			this.label22.AutoSize = true;
-			this.label22.Location = new System.Drawing.Point(3, 85);
+			this.label22.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label22.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label22.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label22.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label22.Location = new System.Drawing.Point(6, 80);
 			this.label22.Name = "label22";
 			this.label22.Size = new System.Drawing.Size(43, 13);
 			this.label22.TabIndex = 94;
@@ -182,26 +240,41 @@
 			// 
 			// txtDescription
 			// 
-			this.txtDescription.Location = new System.Drawing.Point(72, 268);
+			this.txtDescription.BackColor = System.Drawing.Color.White;
+			this.txtDescription.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtDescription.ForeColor = System.Drawing.Color.Black;
+			this.txtDescription.Location = new System.Drawing.Point(107, 130);
 			this.txtDescription.Multiline = true;
 			this.txtDescription.Name = "txtDescription";
-			this.txtDescription.Size = new System.Drawing.Size(503, 90);
-			this.txtDescription.TabIndex = 99;
+			this.txtDescription.Size = new System.Drawing.Size(498, 90);
+			this.txtDescription.TabIndex = 13;
 			// 
 			// cboDefaultPic
 			// 
+			this.cboDefaultPic.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboDefaultPic.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboDefaultPic.BackColor = System.Drawing.Color.White;
 			this.cboDefaultPic.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboDefaultPic.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboDefaultPic.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboDefaultPic.FormattingEnabled = true;
-			this.cboDefaultPic.Location = new System.Drawing.Point(72, 82);
+			this.cboDefaultPic.Location = new System.Drawing.Point(107, 76);
 			this.cboDefaultPic.Name = "cboDefaultPic";
-			this.cboDefaultPic.Size = new System.Drawing.Size(156, 21);
-			this.cboDefaultPic.TabIndex = 89;
+			this.cboDefaultPic.SelectedIndex = -1;
+			this.cboDefaultPic.SelectedItem = null;
+			this.cboDefaultPic.Size = new System.Drawing.Size(212, 21);
+			this.cboDefaultPic.Sorted = false;
+			this.cboDefaultPic.TabIndex = 10;
 			this.cboDefaultPic.SelectedIndexChanged += new System.EventHandler(this.cboDefaultPic_SelectedIndexChanged);
 			// 
 			// label24
 			// 
 			this.label24.AutoSize = true;
-			this.label24.Location = new System.Drawing.Point(3, 271);
+			this.label24.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label24.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label24.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label24.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label24.Location = new System.Drawing.Point(6, 134);
 			this.label24.Name = "label24";
 			this.label24.Size = new System.Drawing.Size(63, 13);
 			this.label24.TabIndex = 105;
@@ -209,22 +282,32 @@
 			// 
 			// txtHeight
 			// 
-			this.txtHeight.Location = new System.Drawing.Point(363, 135);
+			this.txtHeight.BackColor = System.Drawing.Color.White;
+			this.txtHeight.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtHeight.ForeColor = System.Drawing.Color.Black;
+			this.txtHeight.Location = new System.Drawing.Point(392, 76);
 			this.txtHeight.Name = "txtHeight";
-			this.txtHeight.Size = new System.Drawing.Size(62, 20);
-			this.txtHeight.TabIndex = 97;
+			this.txtHeight.Size = new System.Drawing.Size(114, 20);
+			this.txtHeight.TabIndex = 11;
 			// 
 			// txtLastName
 			// 
-			this.txtLastName.Location = new System.Drawing.Point(243, 29);
+			this.txtLastName.BackColor = System.Drawing.Color.White;
+			this.txtLastName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtLastName.ForeColor = System.Drawing.Color.Black;
+			this.txtLastName.Location = new System.Drawing.Point(392, 22);
 			this.txtLastName.Name = "txtLastName";
-			this.txtLastName.Size = new System.Drawing.Size(100, 20);
-			this.txtLastName.TabIndex = 84;
+			this.txtLastName.Size = new System.Drawing.Size(213, 20);
+			this.txtLastName.TabIndex = 7;
 			// 
 			// label23
 			// 
 			this.label23.AutoSize = true;
-			this.label23.Location = new System.Drawing.Point(293, 138);
+			this.label23.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label23.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label23.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label23.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label23.Location = new System.Drawing.Point(323, 80);
 			this.label23.Name = "label23";
 			this.label23.Size = new System.Drawing.Size(41, 13);
 			this.label23.TabIndex = 104;
@@ -233,7 +316,11 @@
 			// label3
 			// 
 			this.label3.AutoSize = true;
-			this.label3.Location = new System.Drawing.Point(178, 32);
+			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label3.Location = new System.Drawing.Point(323, 26);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(59, 13);
 			this.label3.TabIndex = 85;
@@ -241,15 +328,22 @@
 			// 
 			// txtFirstName
 			// 
-			this.txtFirstName.Location = new System.Drawing.Point(72, 29);
+			this.txtFirstName.BackColor = System.Drawing.Color.White;
+			this.txtFirstName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtFirstName.ForeColor = System.Drawing.Color.Black;
+			this.txtFirstName.Location = new System.Drawing.Point(107, 22);
 			this.txtFirstName.Name = "txtFirstName";
-			this.txtFirstName.Size = new System.Drawing.Size(100, 20);
-			this.txtFirstName.TabIndex = 83;
+			this.txtFirstName.Size = new System.Drawing.Size(212, 20);
+			this.txtFirstName.TabIndex = 6;
 			// 
 			// label2
 			// 
 			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(3, 32);
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label2.Location = new System.Drawing.Point(6, 26);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(58, 13);
 			this.label2.TabIndex = 82;
@@ -258,7 +352,11 @@
 			// label19
 			// 
 			this.label19.AutoSize = true;
-			this.label19.Location = new System.Drawing.Point(293, 112);
+			this.label19.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label19.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label19.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label19.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label19.Location = new System.Drawing.Point(6, 54);
 			this.label19.Name = "label19";
 			this.label19.Size = new System.Drawing.Size(33, 13);
 			this.label19.TabIndex = 102;
@@ -266,15 +364,22 @@
 			// 
 			// txtArtist
 			// 
-			this.txtArtist.Location = new System.Drawing.Point(363, 109);
+			this.txtArtist.BackColor = System.Drawing.Color.White;
+			this.txtArtist.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtArtist.ForeColor = System.Drawing.Color.Black;
+			this.txtArtist.Location = new System.Drawing.Point(107, 50);
 			this.txtArtist.Name = "txtArtist";
 			this.txtArtist.Size = new System.Drawing.Size(212, 20);
-			this.txtArtist.TabIndex = 93;
+			this.txtArtist.TabIndex = 15;
 			// 
 			// label18
 			// 
 			this.label18.AutoSize = true;
-			this.label18.Location = new System.Drawing.Point(3, 112);
+			this.label18.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label18.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label18.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label18.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label18.Location = new System.Drawing.Point(6, 27);
 			this.label18.Name = "label18";
 			this.label18.Size = new System.Drawing.Size(38, 13);
 			this.label18.TabIndex = 101;
@@ -282,15 +387,22 @@
 			// 
 			// txtWriter
 			// 
-			this.txtWriter.Location = new System.Drawing.Point(72, 109);
+			this.txtWriter.BackColor = System.Drawing.Color.White;
+			this.txtWriter.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtWriter.ForeColor = System.Drawing.Color.Black;
+			this.txtWriter.Location = new System.Drawing.Point(107, 23);
 			this.txtWriter.Name = "txtWriter";
 			this.txtWriter.Size = new System.Drawing.Size(212, 20);
-			this.txtWriter.TabIndex = 91;
+			this.txtWriter.TabIndex = 14;
 			// 
 			// label17
 			// 
 			this.label17.AutoSize = true;
-			this.label17.Location = new System.Drawing.Point(3, 138);
+			this.label17.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label17.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label17.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label17.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label17.Location = new System.Drawing.Point(6, 107);
 			this.label17.Name = "label17";
 			this.label17.Size = new System.Drawing.Size(37, 13);
 			this.label17.TabIndex = 100;
@@ -298,22 +410,28 @@
 			// 
 			// txtSource
 			// 
-			this.txtSource.Location = new System.Drawing.Point(72, 135);
+			this.txtSource.BackColor = System.Drawing.Color.White;
+			this.txtSource.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtSource.ForeColor = System.Drawing.Color.Black;
+			this.txtSource.Location = new System.Drawing.Point(107, 103);
 			this.txtSource.Name = "txtSource";
 			this.txtSource.Size = new System.Drawing.Size(212, 20);
-			this.txtSource.TabIndex = 95;
+			this.txtSource.TabIndex = 12;
 			// 
 			// valRounds
 			// 
-			this.valRounds.Location = new System.Drawing.Point(452, 56);
+			this.valRounds.BackColor = System.Drawing.Color.White;
+			this.valRounds.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valRounds.ForeColor = System.Drawing.Color.Black;
+			this.valRounds.Location = new System.Drawing.Point(107, 134);
 			this.valRounds.Minimum = new decimal(new int[] {
             1,
             0,
             0,
             0});
 			this.valRounds.Name = "valRounds";
-			this.valRounds.Size = new System.Drawing.Size(59, 20);
-			this.valRounds.TabIndex = 88;
+			this.valRounds.Size = new System.Drawing.Size(62, 20);
+			this.valRounds.TabIndex = 17;
 			this.valRounds.Value = new decimal(new int[] {
             1,
             0,
@@ -323,7 +441,11 @@
 			// label12
 			// 
 			this.label12.AutoSize = true;
-			this.label12.Location = new System.Drawing.Point(360, 58);
+			this.label12.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label12.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label12.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label12.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label12.Location = new System.Drawing.Point(6, 137);
 			this.label12.Name = "label12";
 			this.label12.Size = new System.Drawing.Size(86, 13);
 			this.label12.TabIndex = 96;
@@ -331,96 +453,123 @@
 			// 
 			// cboSize
 			// 
+			this.cboSize.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboSize.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboSize.BackColor = System.Drawing.Color.White;
 			this.cboSize.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboSize.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboSize.FormattingEnabled = true;
-			this.cboSize.Items.AddRange(new object[] {
-            "small",
-            "medium",
-            "large"});
-			this.cboSize.Location = new System.Drawing.Point(243, 55);
+			this.cboSize.Location = new System.Drawing.Point(392, 49);
 			this.cboSize.Name = "cboSize";
-			this.cboSize.Size = new System.Drawing.Size(100, 21);
-			this.cboSize.TabIndex = 87;
+			this.cboSize.SelectedIndex = -1;
+			this.cboSize.SelectedItem = null;
+			this.cboSize.Size = new System.Drawing.Size(114, 21);
+			this.cboSize.Sorted = false;
+			this.cboSize.TabIndex = 9;
 			// 
 			// label10
 			// 
 			this.label10.AutoSize = true;
-			this.label10.Location = new System.Drawing.Point(3, 58);
+			this.label10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label10.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label10.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label10.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label10.Location = new System.Drawing.Point(6, 53);
 			this.label10.Name = "label10";
 			this.label10.Size = new System.Drawing.Size(45, 13);
 			this.label10.TabIndex = 90;
 			this.label10.Text = "Gender:";
 			// 
-			// label11
+			// lblSize
 			// 
-			this.label11.AutoSize = true;
-			this.label11.Location = new System.Drawing.Point(178, 58);
-			this.label11.Name = "label11";
-			this.label11.Size = new System.Drawing.Size(30, 13);
-			this.label11.TabIndex = 92;
-			this.label11.Text = "Size:";
+			this.lblSize.AutoSize = true;
+			this.lblSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblSize.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.lblSize.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblSize.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.lblSize.Location = new System.Drawing.Point(323, 53);
+			this.lblSize.Name = "lblSize";
+			this.lblSize.Size = new System.Drawing.Size(30, 13);
+			this.lblSize.TabIndex = 92;
+			this.lblSize.Text = "Size:";
 			// 
 			// cboGender
 			// 
+			this.cboGender.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboGender.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboGender.BackColor = System.Drawing.Color.White;
 			this.cboGender.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboGender.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboGender.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboGender.FormattingEnabled = true;
-			this.cboGender.Items.AddRange(new object[] {
-            "female",
-            "male"});
-			this.cboGender.Location = new System.Drawing.Point(72, 55);
+			this.cboGender.Location = new System.Drawing.Point(107, 49);
 			this.cboGender.Name = "cboGender";
-			this.cboGender.Size = new System.Drawing.Size(100, 21);
-			this.cboGender.TabIndex = 86;
-			// 
-			// ColAIStage
-			// 
-			this.ColAIStage.HeaderText = "Stage";
-			this.ColAIStage.MinimumWidth = 50;
-			this.ColAIStage.Name = "ColAIStage";
-			this.ColAIStage.Width = 50;
-			// 
-			// ColDifficulty
-			// 
-			this.ColDifficulty.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
-			this.ColDifficulty.HeaderText = "Intelligence";
-			this.ColDifficulty.Items.AddRange(new object[] {
-            "",
-            "bad",
-            "average",
-            "good",
-            "best"});
-			this.ColDifficulty.Name = "ColDifficulty";
-			this.ColDifficulty.Resizable = System.Windows.Forms.DataGridViewTriState.True;
-			this.ColDifficulty.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
+			this.cboGender.SelectedIndex = -1;
+			this.cboGender.SelectedItem = null;
+			this.cboGender.Size = new System.Drawing.Size(112, 21);
+			this.cboGender.Sorted = false;
+			this.cboGender.TabIndex = 8;
+			this.cboGender.SelectedIndexChanged += new System.EventHandler(this.cboGender_SelectedIndexChanged);
+			// 
+			// skinnedGroupBox1
+			// 
+			this.skinnedGroupBox1.Controls.Add(this.label2);
+			this.skinnedGroupBox1.Controls.Add(this.txtFirstName);
+			this.skinnedGroupBox1.Controls.Add(this.label3);
+			this.skinnedGroupBox1.Controls.Add(this.cboSize);
+			this.skinnedGroupBox1.Controls.Add(this.txtDescription);
+			this.skinnedGroupBox1.Controls.Add(this.lblSize);
+			this.skinnedGroupBox1.Controls.Add(this.label22);
+			this.skinnedGroupBox1.Controls.Add(this.cboDefaultPic);
+			this.skinnedGroupBox1.Controls.Add(this.txtLastName);
+			this.skinnedGroupBox1.Controls.Add(this.label24);
+			this.skinnedGroupBox1.Controls.Add(this.label10);
+			this.skinnedGroupBox1.Controls.Add(this.txtHeight);
+			this.skinnedGroupBox1.Controls.Add(this.cboGender);
+			this.skinnedGroupBox1.Controls.Add(this.label23);
+			this.skinnedGroupBox1.Controls.Add(this.txtSource);
+			this.skinnedGroupBox1.Controls.Add(this.label17);
+			this.skinnedGroupBox1.Location = new System.Drawing.Point(6, 30);
+			this.skinnedGroupBox1.Name = "skinnedGroupBox1";
+			this.skinnedGroupBox1.Size = new System.Drawing.Size(616, 231);
+			this.skinnedGroupBox1.TabIndex = 108;
+			this.skinnedGroupBox1.TabStop = false;
+			this.skinnedGroupBox1.Text = "Demographics";
+			// 
+			// skinnedGroupBox2
+			// 
+			this.skinnedGroupBox2.Controls.Add(this.label18);
+			this.skinnedGroupBox2.Controls.Add(this.txtWriter);
+			this.skinnedGroupBox2.Controls.Add(this.label19);
+			this.skinnedGroupBox2.Controls.Add(this.txtArtist);
+			this.skinnedGroupBox2.Location = new System.Drawing.Point(6, 267);
+			this.skinnedGroupBox2.Name = "skinnedGroupBox2";
+			this.skinnedGroupBox2.Size = new System.Drawing.Size(616, 80);
+			this.skinnedGroupBox2.TabIndex = 109;
+			this.skinnedGroupBox2.TabStop = false;
+			this.skinnedGroupBox2.Text = "Credits";
+			// 
+			// skinnedGroupBox3
+			// 
+			this.skinnedGroupBox3.Controls.Add(this.label12);
+			this.skinnedGroupBox3.Controls.Add(this.valRounds);
+			this.skinnedGroupBox3.Controls.Add(this.gridAI);
+			this.skinnedGroupBox3.Controls.Add(this.label7);
+			this.skinnedGroupBox3.Location = new System.Drawing.Point(6, 353);
+			this.skinnedGroupBox3.Name = "skinnedGroupBox3";
+			this.skinnedGroupBox3.Size = new System.Drawing.Size(616, 163);
+			this.skinnedGroupBox3.TabIndex = 110;
+			this.skinnedGroupBox3.TabStop = false;
+			this.skinnedGroupBox3.Text = "Gameplay";
 			// 
 			// MetadataEditor
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.gridAI);
-			this.Controls.Add(this.label7);
-			this.Controls.Add(this.label22);
-			this.Controls.Add(this.txtDescription);
-			this.Controls.Add(this.cboDefaultPic);
-			this.Controls.Add(this.label24);
-			this.Controls.Add(this.txtHeight);
-			this.Controls.Add(this.txtLastName);
-			this.Controls.Add(this.label23);
-			this.Controls.Add(this.label3);
-			this.Controls.Add(this.txtFirstName);
-			this.Controls.Add(this.label2);
-			this.Controls.Add(this.label19);
-			this.Controls.Add(this.txtArtist);
-			this.Controls.Add(this.label18);
-			this.Controls.Add(this.txtWriter);
-			this.Controls.Add(this.label17);
-			this.Controls.Add(this.txtSource);
-			this.Controls.Add(this.valRounds);
-			this.Controls.Add(this.label12);
-			this.Controls.Add(this.cboSize);
-			this.Controls.Add(this.label10);
-			this.Controls.Add(this.label11);
-			this.Controls.Add(this.cboGender);
+			this.Controls.Add(this.skinnedGroupBox3);
+			this.Controls.Add(this.skinnedGroupBox2);
+			this.Controls.Add(this.skinnedGroupBox1);
 			this.Controls.Add(this.lblUnlisted);
 			this.Controls.Add(this.lblTesting);
 			this.Controls.Add(this.lblIncomplete);
@@ -431,6 +580,12 @@
 			this.Size = new System.Drawing.Size(897, 589);
 			((System.ComponentModel.ISupportInitialize)(this.gridAI)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.valRounds)).EndInit();
+			this.skinnedGroupBox1.ResumeLayout(false);
+			this.skinnedGroupBox1.PerformLayout();
+			this.skinnedGroupBox2.ResumeLayout(false);
+			this.skinnedGroupBox2.PerformLayout();
+			this.skinnedGroupBox3.ResumeLayout(false);
+			this.skinnedGroupBox3.PerformLayout();
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -438,37 +593,40 @@
 
 		#endregion
 
-		private System.Windows.Forms.TextBox txtLabel;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label lblUnlisted;
-		private System.Windows.Forms.Label lblTesting;
-		private System.Windows.Forms.Label lblIncomplete;
-		private System.Windows.Forms.Label lblOffline;
-		private System.Windows.Forms.DataGridView gridAI;
-		private System.Windows.Forms.Label label7;
-		private System.Windows.Forms.Label label22;
-		private System.Windows.Forms.TextBox txtDescription;
-		private System.Windows.Forms.ComboBox cboDefaultPic;
-		private System.Windows.Forms.Label label24;
-		private System.Windows.Forms.TextBox txtHeight;
-		private System.Windows.Forms.TextBox txtLastName;
-		private System.Windows.Forms.Label label23;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.TextBox txtFirstName;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.Label label19;
-		private System.Windows.Forms.TextBox txtArtist;
-		private System.Windows.Forms.Label label18;
-		private System.Windows.Forms.TextBox txtWriter;
-		private System.Windows.Forms.Label label17;
-		private System.Windows.Forms.TextBox txtSource;
-		private System.Windows.Forms.NumericUpDown valRounds;
-		private System.Windows.Forms.Label label12;
-		private System.Windows.Forms.ComboBox cboSize;
-		private System.Windows.Forms.Label label10;
-		private System.Windows.Forms.Label label11;
-		private System.Windows.Forms.ComboBox cboGender;
+		private Desktop.Skinning.SkinnedTextBox txtLabel;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel lblUnlisted;
+		private Desktop.Skinning.SkinnedLabel lblTesting;
+		private Desktop.Skinning.SkinnedLabel lblIncomplete;
+		private Desktop.Skinning.SkinnedLabel lblOffline;
+		private Desktop.Skinning.SkinnedDataGridView gridAI;
+		private Desktop.Skinning.SkinnedLabel label7;
+		private Desktop.Skinning.SkinnedLabel label22;
+		private Desktop.Skinning.SkinnedTextBox txtDescription;
+		private Desktop.Skinning.SkinnedComboBox cboDefaultPic;
+		private Desktop.Skinning.SkinnedLabel label24;
+		private Desktop.Skinning.SkinnedTextBox txtHeight;
+		private Desktop.Skinning.SkinnedTextBox txtLastName;
+		private Desktop.Skinning.SkinnedLabel label23;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedTextBox txtFirstName;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedLabel label19;
+		private Desktop.Skinning.SkinnedTextBox txtArtist;
+		private Desktop.Skinning.SkinnedLabel label18;
+		private Desktop.Skinning.SkinnedTextBox txtWriter;
+		private Desktop.Skinning.SkinnedLabel label17;
+		private Desktop.Skinning.SkinnedTextBox txtSource;
+		private Desktop.Skinning.SkinnedNumericUpDown valRounds;
+		private Desktop.Skinning.SkinnedLabel label12;
+		private Desktop.Skinning.SkinnedComboBox cboSize;
+		private Desktop.Skinning.SkinnedLabel label10;
+		private Desktop.Skinning.SkinnedLabel lblSize;
+		private Desktop.Skinning.SkinnedComboBox cboGender;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColAIStage;
-		private System.Windows.Forms.DataGridViewComboBoxColumn ColDifficulty;
+		private Desktop.Skinning.SkinnedDataGridViewComboBoxColumn ColDifficulty;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox1;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox2;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox3;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/MetadataEditor.cs b/editor source/SPNATI Character Editor/Activities/MetadataEditor.cs
index 85760836b48ce832377f4b815f005a1bb09fd3e4..a34b606346765e76ed08581aa0b88b0e188524c5 100644
--- a/editor source/SPNATI Character Editor/Activities/MetadataEditor.cs	
+++ b/editor source/SPNATI Character Editor/Activities/MetadataEditor.cs	
@@ -15,6 +15,10 @@ namespace SPNATI_Character_Editor.Activities
 		public MetadataEditor()
 		{
 			InitializeComponent();
+
+			cboGender.Items.AddRange(new string[] { "female", "male" });
+			cboSize.Items.AddRange(new string[] { "small", "medium", "large" });
+			ColDifficulty.Items.AddRange(DialogueLine.AILevels);
 		}
 
 		public override string Caption
@@ -69,11 +73,9 @@ namespace SPNATI_Character_Editor.Activities
 			images.AddRange(_imageLibrary.GetImages(0));
 			if (Config.UsePrefixlessImages)
 			{
-				string prefix = Config.PrefixFilter;
 				foreach (CharacterImage img in _imageLibrary.GetImages(-1))
 				{
-					string file = img.Name;
-					if (string.IsNullOrEmpty(prefix) || !file.StartsWith(prefix))
+					if (!_imageLibrary.FilterImage(_character, img.Name))
 					{
 						images.Add(img);
 					}
@@ -148,5 +150,18 @@ namespace SPNATI_Character_Editor.Activities
 			_character.Metadata.Portrait = image.Name + image.FileExtension;
 			Workspace.SendMessage(WorkspaceMessages.UpdatePreviewImage, _imageLibrary.Find(_character.Metadata.Portrait));
 		}
+
+		private void cboGender_SelectedIndexChanged(object sender, System.EventArgs e)
+		{
+			string gender = cboGender.SelectedItem?.ToString();
+			if (gender == "male")
+			{
+				lblSize.Text = "Crotch:";
+			}
+			else
+			{
+				lblSize.Text = "Chest:";
+			}
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/PoseListEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/PoseListEditor.Designer.cs
index 80ac051accaedaa55f3b0cc5310871efb8164660..41c518404e62e49df44081c8743b12dd30d260fb 100644
--- a/editor source/SPNATI Character Editor/Activities/PoseListEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/PoseListEditor.Designer.cs	
@@ -29,25 +29,17 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
-			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle();
-			this.cmdImportAll = new System.Windows.Forms.Button();
-			this.cmdImportNew = new System.Windows.Forms.Button();
-			this.cmdClear = new System.Windows.Forms.Button();
-			this.cmdExport = new System.Windows.Forms.Button();
-			this.cmdImport = new System.Windows.Forms.Button();
-			this.gridPoses = new System.Windows.Forms.DataGridView();
-			this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
-			this.cutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-			this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-			this.pasteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-			this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
-			this.duplicateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-			this.label5 = new System.Windows.Forms.Label();
-			this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
-			this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog();
-			this.lblCurrentPoseFile = new System.Windows.Forms.Label();
-			this.chkRequired = new System.Windows.Forms.CheckBox();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle6 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle9 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle10 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle8 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.cmdImportAll = new Desktop.Skinning.SkinnedButton();
+			this.cmdImportNew = new Desktop.Skinning.SkinnedButton();
+			this.cmdClear = new Desktop.Skinning.SkinnedButton();
+			this.cmdExport = new Desktop.Skinning.SkinnedButton();
+			this.cmdImport = new Desktop.Skinning.SkinnedButton();
+			this.gridPoses = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColStage = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColPose = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColL = new System.Windows.Forms.DataGridViewTextBoxColumn();
@@ -55,9 +47,22 @@
 			this.ColR = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColB = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColData = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColAdvanced = new System.Windows.Forms.DataGridViewButtonColumn();
+			this.ColAdvanced = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
 			this.ColImage = new System.Windows.Forms.DataGridViewImageColumn();
-			this.ColImport = new System.Windows.Forms.DataGridViewButtonColumn();
+			this.ColImport = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
+			this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
+			this.cutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.pasteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+			this.duplicateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
+			this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
+			this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog();
+			this.lblCurrentPoseFile = new Desktop.Skinning.SkinnedLabel();
+			this.chkRequired = new Desktop.Skinning.SkinnedCheckBox();
+			this.cmdCopyCrop = new Desktop.Skinning.SkinnedButton();
+			this.cmdFolder = new Desktop.Skinning.SkinnedButton();
 			((System.ComponentModel.ISupportInitialize)(this.gridPoses)).BeginInit();
 			this.contextMenu.SuspendLayout();
 			this.SuspendLayout();
@@ -65,6 +70,9 @@
 			// cmdImportAll
 			// 
 			this.cmdImportAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdImportAll.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdImportAll.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdImportAll.Flat = false;
 			this.cmdImportAll.Location = new System.Drawing.Point(843, 3);
 			this.cmdImportAll.Name = "cmdImportAll";
 			this.cmdImportAll.Size = new System.Drawing.Size(95, 23);
@@ -76,6 +84,9 @@
 			// cmdImportNew
 			// 
 			this.cmdImportNew.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdImportNew.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdImportNew.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdImportNew.Flat = false;
 			this.cmdImportNew.Location = new System.Drawing.Point(742, 3);
 			this.cmdImportNew.Name = "cmdImportNew";
 			this.cmdImportNew.Size = new System.Drawing.Size(95, 23);
@@ -86,7 +97,11 @@
 			// 
 			// cmdClear
 			// 
-			this.cmdClear.Location = new System.Drawing.Point(205, 3);
+			this.cmdClear.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdClear.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdClear.Flat = true;
+			this.cmdClear.ForeColor = System.Drawing.Color.Blue;
+			this.cmdClear.Location = new System.Drawing.Point(237, 3);
 			this.cmdClear.Name = "cmdClear";
 			this.cmdClear.Size = new System.Drawing.Size(54, 23);
 			this.cmdClear.TabIndex = 23;
@@ -96,9 +111,12 @@
 			// 
 			// cmdExport
 			// 
-			this.cmdExport.Location = new System.Drawing.Point(104, 3);
+			this.cmdExport.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdExport.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdExport.Flat = false;
+			this.cmdExport.Location = new System.Drawing.Point(120, 3);
 			this.cmdExport.Name = "cmdExport";
-			this.cmdExport.Size = new System.Drawing.Size(95, 23);
+			this.cmdExport.Size = new System.Drawing.Size(113, 23);
 			this.cmdExport.TabIndex = 22;
 			this.cmdExport.Text = "Save Pose List";
 			this.cmdExport.UseVisualStyleBackColor = true;
@@ -106,9 +124,12 @@
 			// 
 			// cmdImport
 			// 
+			this.cmdImport.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdImport.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdImport.Flat = false;
 			this.cmdImport.Location = new System.Drawing.Point(3, 3);
 			this.cmdImport.Name = "cmdImport";
-			this.cmdImport.Size = new System.Drawing.Size(95, 23);
+			this.cmdImport.Size = new System.Drawing.Size(113, 23);
 			this.cmdImport.TabIndex = 21;
 			this.cmdImport.Text = "Load Pose List";
 			this.cmdImport.UseVisualStyleBackColor = true;
@@ -119,7 +140,19 @@
 			this.gridPoses.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridPoses.BackgroundColor = System.Drawing.Color.White;
+			this.gridPoses.BorderStyle = System.Windows.Forms.BorderStyle.None;
 			this.gridPoses.ClipboardCopyMode = System.Windows.Forms.DataGridViewClipboardCopyMode.EnableWithoutHeaderText;
+			this.gridPoses.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle6.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle6.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle6.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle6.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle6.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle6.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle6.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridPoses.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle6;
 			this.gridPoses.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridPoses.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColStage,
@@ -133,22 +166,120 @@
             this.ColImage,
             this.ColImport});
 			this.gridPoses.ContextMenuStrip = this.contextMenu;
+			dataGridViewCellStyle9.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle9.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle9.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle9.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle9.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle9.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridPoses.DefaultCellStyle = dataGridViewCellStyle9;
 			this.gridPoses.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
-			this.gridPoses.Location = new System.Drawing.Point(3, 32);
+			this.gridPoses.EnableHeadersVisualStyles = false;
+			this.gridPoses.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridPoses.GridColor = System.Drawing.Color.LightGray;
+			this.gridPoses.Location = new System.Drawing.Point(3, 61);
 			this.gridPoses.MultiSelect = false;
 			this.gridPoses.Name = "gridPoses";
+			this.gridPoses.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle10.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle10.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle10.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle10.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle10.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle10.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridPoses.RowHeadersDefaultCellStyle = dataGridViewCellStyle10;
 			this.gridPoses.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.DisableResizing;
 			this.gridPoses.RowTemplate.DefaultCellStyle.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
 			this.gridPoses.RowTemplate.Height = 100;
-			this.gridPoses.Size = new System.Drawing.Size(935, 591);
+			this.gridPoses.Size = new System.Drawing.Size(935, 562);
 			this.gridPoses.TabIndex = 20;
 			this.gridPoses.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridPoses_CellContentClick);
+			this.gridPoses.CellEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridPoses_CellEnter);
 			this.gridPoses.ColumnHeaderMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.gridPoses_ColumnHeaderMouseClick);
 			this.gridPoses.RowPrePaint += new System.Windows.Forms.DataGridViewRowPrePaintEventHandler(this.gridPoses_RowPrePaint);
 			this.gridPoses.RowsAdded += new System.Windows.Forms.DataGridViewRowsAddedEventHandler(this.gridPoses_RowsAdded);
 			this.gridPoses.Scroll += new System.Windows.Forms.ScrollEventHandler(this.gridPoses_Scroll);
 			this.gridPoses.MouseDown += new System.Windows.Forms.MouseEventHandler(this.gridPoses_MouseDown);
 			// 
+			// ColStage
+			// 
+			this.ColStage.HeaderText = "Stage";
+			this.ColStage.Name = "ColStage";
+			this.ColStage.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Programmatic;
+			this.ColStage.Width = 50;
+			// 
+			// ColPose
+			// 
+			this.ColPose.HeaderText = "Pose";
+			this.ColPose.Name = "ColPose";
+			this.ColPose.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Programmatic;
+			// 
+			// ColL
+			// 
+			this.ColL.HeaderText = "L";
+			this.ColL.Name = "ColL";
+			this.ColL.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+			this.ColL.Width = 40;
+			// 
+			// ColT
+			// 
+			this.ColT.HeaderText = "T";
+			this.ColT.Name = "ColT";
+			this.ColT.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+			this.ColT.Width = 40;
+			// 
+			// ColR
+			// 
+			this.ColR.HeaderText = "R";
+			this.ColR.Name = "ColR";
+			this.ColR.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+			this.ColR.Width = 40;
+			// 
+			// ColB
+			// 
+			this.ColB.HeaderText = "B";
+			this.ColB.Name = "ColB";
+			this.ColB.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+			this.ColB.Width = 40;
+			// 
+			// ColData
+			// 
+			this.ColData.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.TopLeft;
+			dataGridViewCellStyle7.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.ColData.DefaultCellStyle = dataGridViewCellStyle7;
+			this.ColData.HeaderText = "Code";
+			this.ColData.Name = "ColData";
+			this.ColData.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+			// 
+			// ColAdvanced
+			// 
+			dataGridViewCellStyle8.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
+			dataGridViewCellStyle8.NullValue = "More...";
+			this.ColAdvanced.DefaultCellStyle = dataGridViewCellStyle8;
+			this.ColAdvanced.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColAdvanced.Flat = false;
+			this.ColAdvanced.HeaderText = "";
+			this.ColAdvanced.Name = "ColAdvanced";
+			this.ColAdvanced.Resizable = System.Windows.Forms.DataGridViewTriState.True;
+			this.ColAdvanced.Width = 70;
+			// 
+			// ColImage
+			// 
+			this.ColImage.HeaderText = "Image";
+			this.ColImage.Name = "ColImage";
+			this.ColImage.Width = 75;
+			// 
+			// ColImport
+			// 
+			this.ColImport.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColImport.Flat = false;
+			this.ColImport.HeaderText = "";
+			this.ColImport.Name = "ColImport";
+			this.ColImport.Width = 90;
+			// 
 			// contextMenu
 			// 
 			this.contextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
@@ -202,6 +333,10 @@
 			// label5
 			// 
 			this.label5.AutoSize = true;
+			this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label5.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label5.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label5.Location = new System.Drawing.Point(3, 7);
 			this.label5.Name = "label5";
 			this.label5.Size = new System.Drawing.Size(77, 13);
@@ -219,7 +354,11 @@
 			// lblCurrentPoseFile
 			// 
 			this.lblCurrentPoseFile.AutoSize = true;
-			this.lblCurrentPoseFile.Location = new System.Drawing.Point(265, 7);
+			this.lblCurrentPoseFile.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblCurrentPoseFile.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblCurrentPoseFile.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblCurrentPoseFile.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblCurrentPoseFile.Location = new System.Drawing.Point(297, 7);
 			this.lblCurrentPoseFile.Name = "lblCurrentPoseFile";
 			this.lblCurrentPoseFile.Size = new System.Drawing.Size(100, 13);
 			this.lblCurrentPoseFile.TabIndex = 34;
@@ -229,90 +368,48 @@
 			// 
 			this.chkRequired.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
 			this.chkRequired.AutoSize = true;
-			this.chkRequired.Location = new System.Drawing.Point(607, 6);
+			this.chkRequired.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkRequired.Location = new System.Drawing.Point(809, 36);
 			this.chkRequired.Name = "chkRequired";
 			this.chkRequired.Size = new System.Drawing.Size(129, 17);
 			this.chkRequired.TabIndex = 35;
 			this.chkRequired.Text = "Include missing poses";
 			this.chkRequired.UseVisualStyleBackColor = true;
 			// 
-			// ColStage
-			// 
-			this.ColStage.HeaderText = "Stage";
-			this.ColStage.Name = "ColStage";
-			this.ColStage.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Programmatic;
-			this.ColStage.Width = 50;
-			// 
-			// ColPose
-			// 
-			this.ColPose.HeaderText = "Pose";
-			this.ColPose.Name = "ColPose";
-			this.ColPose.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Programmatic;
-			// 
-			// ColL
-			// 
-			this.ColL.HeaderText = "L";
-			this.ColL.Name = "ColL";
-			this.ColL.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-			this.ColL.Width = 40;
-			// 
-			// ColT
-			// 
-			this.ColT.HeaderText = "T";
-			this.ColT.Name = "ColT";
-			this.ColT.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-			this.ColT.Width = 40;
-			// 
-			// ColR
-			// 
-			this.ColR.HeaderText = "R";
-			this.ColR.Name = "ColR";
-			this.ColR.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-			this.ColR.Width = 40;
-			// 
-			// ColB
-			// 
-			this.ColB.HeaderText = "B";
-			this.ColB.Name = "ColB";
-			this.ColB.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-			this.ColB.Width = 40;
-			// 
-			// ColData
-			// 
-			this.ColData.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
-			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.TopLeft;
-			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
-			this.ColData.DefaultCellStyle = dataGridViewCellStyle3;
-			this.ColData.HeaderText = "Code";
-			this.ColData.Name = "ColData";
-			this.ColData.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-			// 
-			// ColAdvanced
-			// 
-			dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
-			dataGridViewCellStyle4.NullValue = "More...";
-			this.ColAdvanced.DefaultCellStyle = dataGridViewCellStyle4;
-			this.ColAdvanced.HeaderText = "";
-			this.ColAdvanced.Name = "ColAdvanced";
-			this.ColAdvanced.Resizable = System.Windows.Forms.DataGridViewTriState.True;
-			this.ColAdvanced.Width = 70;
-			// 
-			// ColImage
-			// 
-			this.ColImage.HeaderText = "Image";
-			this.ColImage.Name = "ColImage";
-			this.ColImage.Width = 75;
-			// 
-			// ColImport
-			// 
-			this.ColImport.HeaderText = "";
-			this.ColImport.Name = "ColImport";
-			this.ColImport.Width = 70;
+			// cmdCopyCrop
+			// 
+			this.cmdCopyCrop.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCopyCrop.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCopyCrop.Flat = true;
+			this.cmdCopyCrop.ForeColor = System.Drawing.Color.Blue;
+			this.cmdCopyCrop.Location = new System.Drawing.Point(3, 32);
+			this.cmdCopyCrop.Name = "cmdCopyCrop";
+			this.cmdCopyCrop.Size = new System.Drawing.Size(113, 23);
+			this.cmdCopyCrop.TabIndex = 36;
+			this.cmdCopyCrop.Text = "Copy Cropping";
+			this.cmdCopyCrop.UseVisualStyleBackColor = true;
+			this.cmdCopyCrop.Click += new System.EventHandler(this.cmdCopyCrop_Click);
+			// 
+			// cmdFolder
+			// 
+			this.cmdFolder.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdFolder.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdFolder.Flat = true;
+			this.cmdFolder.ForeColor = System.Drawing.Color.Blue;
+			this.cmdFolder.Location = new System.Drawing.Point(120, 32);
+			this.cmdFolder.Name = "cmdFolder";
+			this.cmdFolder.Size = new System.Drawing.Size(113, 23);
+			this.cmdFolder.TabIndex = 37;
+			this.cmdFolder.Text = "Open Folder";
+			this.cmdFolder.UseVisualStyleBackColor = true;
+			this.cmdFolder.Click += new System.EventHandler(this.cmdFolder_Click);
 			// 
 			// PoseListEditor
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.cmdFolder);
+			this.Controls.Add(this.cmdCopyCrop);
 			this.Controls.Add(this.chkRequired);
 			this.Controls.Add(this.lblCurrentPoseFile);
 			this.Controls.Add(this.cmdImportAll);
@@ -332,23 +429,24 @@
 		}
 
 		#endregion
-		private System.Windows.Forms.Button cmdImportAll;
-		private System.Windows.Forms.Button cmdImportNew;
-		private System.Windows.Forms.Button cmdClear;
-		private System.Windows.Forms.Button cmdExport;
-		private System.Windows.Forms.Button cmdImport;
-		private System.Windows.Forms.DataGridView gridPoses;
-		private System.Windows.Forms.Label label5;
+		private Desktop.Skinning.SkinnedButton cmdImportAll;
+		private Desktop.Skinning.SkinnedButton cmdImportNew;
+		private Desktop.Skinning.SkinnedButton cmdClear;
+		private Desktop.Skinning.SkinnedButton cmdExport;
+		private Desktop.Skinning.SkinnedButton cmdImport;
+		private Desktop.Skinning.SkinnedDataGridView gridPoses;
+		private Desktop.Skinning.SkinnedLabel label5;
 		private System.Windows.Forms.OpenFileDialog openFileDialog1;
 		private System.Windows.Forms.SaveFileDialog saveFileDialog1;
-		private System.Windows.Forms.Label lblCurrentPoseFile;
+		private Desktop.Skinning.SkinnedLabel lblCurrentPoseFile;
 		private System.Windows.Forms.ContextMenuStrip contextMenu;
 		private System.Windows.Forms.ToolStripMenuItem cutToolStripMenuItem;
 		private System.Windows.Forms.ToolStripMenuItem copyToolStripMenuItem;
 		private System.Windows.Forms.ToolStripMenuItem pasteToolStripMenuItem;
 		private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
 		private System.Windows.Forms.ToolStripMenuItem duplicateToolStripMenuItem;
-		private System.Windows.Forms.CheckBox chkRequired;
+		private Desktop.Skinning.SkinnedCheckBox chkRequired;
+		private Desktop.Skinning.SkinnedButton cmdCopyCrop;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColStage;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColPose;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColL;
@@ -356,8 +454,9 @@
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColR;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColB;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColData;
-		private System.Windows.Forms.DataGridViewButtonColumn ColAdvanced;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColAdvanced;
 		private System.Windows.Forms.DataGridViewImageColumn ColImage;
-		private System.Windows.Forms.DataGridViewButtonColumn ColImport;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColImport;
+		private Desktop.Skinning.SkinnedButton cmdFolder;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/PoseListEditor.cs b/editor source/SPNATI Character Editor/Activities/PoseListEditor.cs
index 8a0f3e199e5c83d328a63909a6226c31c5baece0..f887f17fc2eb2584c571615233e1472d5c884473 100644
--- a/editor source/SPNATI Character Editor/Activities/PoseListEditor.cs	
+++ b/editor source/SPNATI Character Editor/Activities/PoseListEditor.cs	
@@ -6,6 +6,7 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Diagnostics;
 using System.Drawing;
 using System.IO;
 using System.Threading;
@@ -40,7 +41,7 @@ namespace SPNATI_Character_Editor.Activities
 		public PoseListEditor()
 		{
 			InitializeComponent();
-
+			ColImport.Flat = ColAdvanced.Flat = true;
 			ColImage.DefaultCellStyle.NullValue = null;
 		}
 
@@ -644,6 +645,21 @@ namespace SPNATI_Character_Editor.Activities
 		/// <param name="list"></param>
 		private void ImportPosesAsync(List<ImageMetadata> list)
 		{
+			//find all unrecognized props and assign them all up front
+			List<KisekaeCode> codes = new List<KisekaeCode>();
+			foreach (ImageMetadata metadata in list)
+			{
+				KisekaeCode code = new KisekaeCode(metadata.Data, true);
+				if (code.TotalAssets > 0)
+				{
+					codes.Add(code);
+				}
+			}
+			if (!CharacterGenerator.ImportUnrecognizedAssets(_character, codes))
+			{
+				return;
+			}
+
 			ProgressForm progressForm = new ProgressForm();
 			progressForm.Text = "Import New Poses";
 			progressForm.Show(this);
@@ -695,7 +711,9 @@ namespace SPNATI_Character_Editor.Activities
 							if (img == null)
 							{
 								//Something went wrong. Stop here.
-								MessageBox.Show("Couldn't import " + metadata.ImageKey + ". Is Kisekae running?", "Import Pose", MessageBoxButtons.OK, MessageBoxIcon.Error);
+								FailedImport import = new FailedImport();
+								import.ShowDialog();
+								//MessageBox.Show("Couldn't import " + metadata.ImageKey + ". Is Kisekae running?", "Import Pose", MessageBoxButtons.OK, MessageBoxIcon.Error);
 								return -1;
 							}
 
@@ -1042,6 +1060,63 @@ namespace SPNATI_Character_Editor.Activities
 				return compare;
 			}
 		}
+
+		private void gridPoses_CellEnter(object sender, DataGridViewCellEventArgs e)
+		{
+			DataGridViewRow row = gridPoses.Rows[e.RowIndex];
+			string stage = row.Cells["ColStage"].Value?.ToString();
+			string pose = row.Cells["ColPose"].Value?.ToString();
+			if (string.IsNullOrEmpty(pose))
+			{
+				return;
+			}
+			string name = pose;
+			if (!string.IsNullOrEmpty(stage))
+			{
+				name = stage + "-" + pose;
+			}
+
+			CharacterImage img = _imageLibrary.Find(name);
+			if (img != null)
+			{
+				Workspace.SendMessage(WorkspaceMessages.UpdatePreviewImage, img);
+			}
+		}
+
+		private void cmdCopyCrop_Click(object sender, EventArgs e)
+		{
+			MakePoseList();
+			ImageMetadata selected = null;
+			if (gridPoses.SelectedCells.Count > 0)
+			{
+				int index = gridPoses.SelectedCells[0].RowIndex;
+				selected = index < _poseList.Poses.Count ? _poseList.Poses[index] : null;
+			}
+			CropCopier copier = new CropCopier(_poseList, selected);
+			if (copier.ShowDialog() == DialogResult.OK)
+			{
+				PopulatePoseGrid();
+			}
+		}
+
+		private void cmdFolder_Click(object sender, EventArgs e)
+		{
+			string directory = _character.GetDirectory();
+			try
+			{
+				ProcessStartInfo startInfo = new ProcessStartInfo()
+				{
+					Arguments = directory,
+					FileName = "explorer.exe"
+				};
+
+				Process.Start(startInfo);
+			}
+			catch (Exception ex)
+			{
+				MessageBox.Show(ex.Message);
+			}
+		}
 	}
 
 }
diff --git a/editor source/SPNATI Character Editor/Activities/PoseUsageGraph.Designer.cs b/editor source/SPNATI Character Editor/Activities/PoseUsageGraph.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2e5cfe8fb3724a6b82ef0b67e1cce89ad07664ba
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/PoseUsageGraph.Designer.cs	
@@ -0,0 +1,99 @@
+namespace SPNATI_Character_Editor.Activities
+{
+	partial class PoseUsageGraph
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
+			System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
+			System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
+			System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series();
+			this.graph = new System.Windows.Forms.DataVisualization.Charting.Chart();
+			this.flowStages = new System.Windows.Forms.FlowLayoutPanel();
+			((System.ComponentModel.ISupportInitialize)(this.graph)).BeginInit();
+			this.SuspendLayout();
+			// 
+			// graph
+			// 
+			this.graph.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			chartArea1.AxisX.MajorGrid.Enabled = false;
+			chartArea1.AxisX.ScaleView.Size = 8D;
+			chartArea1.AxisY.MajorGrid.Interval = 0D;
+			chartArea1.AxisY.MinorGrid.Interval = 5D;
+			chartArea1.AxisY.MinorGrid.LineColor = System.Drawing.Color.Silver;
+			chartArea1.Name = "ChartArea1";
+			this.graph.ChartAreas.Add(chartArea1);
+			legend1.Name = "Legend1";
+			this.graph.Legends.Add(legend1);
+			this.graph.Location = new System.Drawing.Point(3, 64);
+			this.graph.Name = "graph";
+			series1.ChartArea = "ChartArea1";
+			series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;
+			series1.Legend = "Legend1";
+			series1.Name = "Series1";
+			series1.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.String;
+			series2.ChartArea = "ChartArea1";
+			series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;
+			series2.Legend = "Legend1";
+			series2.Name = "Series2";
+			series2.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.String;
+			this.graph.Series.Add(series1);
+			this.graph.Series.Add(series2);
+			this.graph.Size = new System.Drawing.Size(1015, 596);
+			this.graph.TabIndex = 0;
+			this.graph.Text = "chart1";
+			// 
+			// flowStages
+			// 
+			this.flowStages.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.flowStages.Location = new System.Drawing.Point(3, 3);
+			this.flowStages.Name = "flowStages";
+			this.flowStages.Size = new System.Drawing.Size(1015, 55);
+			this.flowStages.TabIndex = 1;
+			// 
+			// PoseUsageGraph
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.flowStages);
+			this.Controls.Add(this.graph);
+			this.Name = "PoseUsageGraph";
+			this.Size = new System.Drawing.Size(1021, 660);
+			((System.ComponentModel.ISupportInitialize)(this.graph)).EndInit();
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private System.Windows.Forms.DataVisualization.Charting.Chart graph;
+		private System.Windows.Forms.FlowLayoutPanel flowStages;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Activities/PoseUsageGraph.cs b/editor source/SPNATI Character Editor/Activities/PoseUsageGraph.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e26351ecd106a6f275f3dba14e4c8a2001b5a8e9
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/PoseUsageGraph.cs	
@@ -0,0 +1,143 @@
+using Desktop;
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+using System.Windows.Forms.DataVisualization.Charting;
+using Desktop.Skinning;
+
+namespace SPNATI_Character_Editor.Activities
+{
+	[Activity(typeof(Character), 800)]
+	public partial class PoseUsageGraph : Activity
+	{
+		private Character _character;
+
+		public PoseUsageGraph()
+		{
+			InitializeComponent();
+		}
+
+		protected override void OnInitialize()
+		{
+			_character = Record as Character;
+		}
+
+		public override string Caption
+		{
+			get { return "Pose Usage"; }
+		}
+
+		protected override void OnActivate()
+		{
+			RefreshGraph();
+		}
+
+		private void RefreshGraph()
+		{
+			HashSet<int> checkedStages = new HashSet<int>();
+			foreach (Control ctl in flowStages.Controls)
+			{
+				CheckBox box = ctl as CheckBox;
+				if (box.Checked)
+				{
+					int stage = (int)ctl.Tag;
+					checkedStages.Add(stage);
+				}
+			}
+
+			flowStages.Controls.Clear();
+			graph.Series.Clear();
+
+			//pose name, stage, count
+			DualKeyDictionary<string, int, int> data = new DualKeyDictionary<string, int, int>();
+
+			for (int i = 0; i < _character.Layers + Clothing.ExtraStages; i++)
+			{
+				string stageName = _character.LayerToStageName(i).DisplayName;
+				Series series = graph.Series.Add(stageName);
+				series.ChartType = SeriesChartType.StackedColumn;
+
+				CheckBox box = new CheckBox();
+				flowStages.Controls.Add(box);
+				box.Tag = i;
+				box.Text = stageName;
+				box.Checked = (checkedStages.Count == 0 || checkedStages.Contains(i));
+				box.CheckedChanged += Box_CheckedChanged;
+			}
+
+			foreach (Case workingCase in _character.Behavior.GetWorkingCases())
+			{
+				foreach (DialogueLine line in workingCase.Lines)
+				{
+					string image = line.Image;
+					if (string.IsNullOrEmpty(image)) { continue; }
+					for (int stage = 0; stage < _character.Layers + Clothing.ExtraStages; stage++)
+					{
+						int count = data.Get(image, stage);
+						if (workingCase.Stages.Contains(stage))
+						{
+							count++;
+						}
+						data.Set(image, stage, count);
+					}
+				}
+			}
+			List<Tuple<string, int, int>> results = new List<Tuple<string, int, int>>();
+			foreach (KeyValuePair<string, Dictionary<int, int>> kvp1 in data)
+			{
+				foreach (KeyValuePair<int, int> kvp2 in kvp1.Value)
+				{
+					results.Add(new Tuple<string, int, int>(kvp1.Key, kvp2.Key, kvp2.Value));
+				}
+			}
+			results.Sort((a, b) =>
+			{
+				return a.Item1.CompareTo(b.Item1);
+			});
+			foreach (Tuple<string, int, int> result in results)
+			{
+				Series series = graph.Series[result.Item2];
+				DataPointCollection points = series.Points;
+				DataPoint pt = points[points.AddXY(result.Item1, result.Item3)];
+				pt.ToolTip = result.Item1;
+			}
+		}
+
+		private void Box_CheckedChanged(object sender, EventArgs e)
+		{
+			CheckBox box = sender as CheckBox;
+			int stage = (int)box.Tag;
+			graph.Series[stage].Enabled = box.Checked;
+		}
+
+		protected override void OnSkinChanged(Skin skin)
+		{
+			base.OnSkinChanged(skin);
+			graph.BackColor = skin.Background.Normal;
+			graph.ForeColor = skin.Background.ForeColor;
+			foreach (ChartArea area in graph.ChartAreas)
+			{
+				area.BackColor = skin.FieldBackColor;
+				foreach (Axis axis in area.Axes)
+				{
+					axis.TitleForeColor = skin.Background.ForeColor;
+					axis.LabelStyle.ForeColor = skin.Background.ForeColor;
+				}
+			}
+			foreach (Legend legend in graph.Legends)
+			{
+				legend.BackColor = skin.Background.Normal;
+				legend.ForeColor = skin.Background.ForeColor;
+			}
+			foreach (Title title in graph.Titles)
+			{
+				title.BackColor = skin.Background.Normal;
+				title.ForeColor = skin.Background.ForeColor;
+			}
+			foreach (Series series in graph.Series)
+			{
+				series.LabelForeColor = skin.Background.ForeColor;
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterPersistentMarkerControl.resx b/editor source/SPNATI Character Editor/Activities/PoseUsageGraph.resx
similarity index 100%
rename from editor source/SPNATI Character Editor/Controls/EditControls/CharacterPersistentMarkerControl.resx
rename to editor source/SPNATI Character Editor/Activities/PoseUsageGraph.resx
diff --git a/editor source/SPNATI Character Editor/Activities/RecipeEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/RecipeEditor.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..72d94bc51fd263195f8269dfce006ebc374230d8
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/RecipeEditor.Designer.cs	
@@ -0,0 +1,342 @@
+namespace SPNATI_Character_Editor.Activities
+{
+	partial class RecipeEditor
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.txtName = new Desktop.Skinning.SkinnedTextBox();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.txtDescription = new Desktop.Skinning.SkinnedTextBox();
+			this.cmdOpen = new Desktop.Skinning.SkinnedButton();
+			this.tableConditions = new Desktop.CommonControls.PropertyTable();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
+			this.cboTag = new Desktop.Skinning.SkinnedComboBox();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
+			this.txtFile = new Desktop.Skinning.SkinnedTextBox();
+			this.label6 = new Desktop.Skinning.SkinnedLabel();
+			this.txtGroup = new Desktop.Skinning.SkinnedTextBox();
+			this.skinnedGroupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.skinnedGroupBox2 = new Desktop.Skinning.SkinnedGroupBox();
+			this.skinnedGroupBox3 = new Desktop.Skinning.SkinnedGroupBox();
+			this.gridLines = new SPNATI_Character_Editor.Controls.DialogueGrid();
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.txtLabel = new Desktop.Skinning.SkinnedTextBox();
+			this.skinnedGroupBox1.SuspendLayout();
+			this.skinnedGroupBox2.SuspendLayout();
+			this.skinnedGroupBox3.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// label1
+			// 
+			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(6, 28);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(38, 13);
+			this.label1.TabIndex = 0;
+			this.label1.Text = "Name:";
+			// 
+			// txtName
+			// 
+			this.txtName.BackColor = System.Drawing.Color.White;
+			this.txtName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtName.ForeColor = System.Drawing.Color.Black;
+			this.txtName.Location = new System.Drawing.Point(82, 25);
+			this.txtName.Name = "txtName";
+			this.txtName.Size = new System.Drawing.Size(402, 20);
+			this.txtName.TabIndex = 1;
+			// 
+			// label2
+			// 
+			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label2.Location = new System.Drawing.Point(6, 80);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(63, 13);
+			this.label2.TabIndex = 2;
+			this.label2.Text = "Description:";
+			// 
+			// txtDescription
+			// 
+			this.txtDescription.BackColor = System.Drawing.Color.White;
+			this.txtDescription.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtDescription.ForeColor = System.Drawing.Color.Black;
+			this.txtDescription.Location = new System.Drawing.Point(82, 77);
+			this.txtDescription.Multiline = true;
+			this.txtDescription.Name = "txtDescription";
+			this.txtDescription.Size = new System.Drawing.Size(799, 79);
+			this.txtDescription.TabIndex = 5;
+			// 
+			// cmdOpen
+			// 
+			this.cmdOpen.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdOpen.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOpen.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOpen.Flat = false;
+			this.cmdOpen.Location = new System.Drawing.Point(777, 23);
+			this.cmdOpen.Name = "cmdOpen";
+			this.cmdOpen.Size = new System.Drawing.Size(104, 23);
+			this.cmdOpen.TabIndex = 3;
+			this.cmdOpen.Text = "Open Folder";
+			this.cmdOpen.UseVisualStyleBackColor = true;
+			this.cmdOpen.Click += new System.EventHandler(this.cmdOpen_Click);
+			// 
+			// tableConditions
+			// 
+			this.tableConditions.AllowDelete = true;
+			this.tableConditions.AllowFavorites = false;
+			this.tableConditions.AllowHelp = false;
+			this.tableConditions.AllowMacros = false;
+			this.tableConditions.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.tableConditions.BackColor = System.Drawing.Color.White;
+			this.tableConditions.Data = null;
+			this.tableConditions.HeaderType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.tableConditions.HideAddField = false;
+			this.tableConditions.HideSpeedButtons = false;
+			this.tableConditions.Location = new System.Drawing.Point(6, 23);
+			this.tableConditions.ModifyingProperty = null;
+			this.tableConditions.Name = "tableConditions";
+			this.tableConditions.PanelType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.tableConditions.PlaceholderText = "Add a condition...";
+			this.tableConditions.PreserveControls = false;
+			this.tableConditions.PreviewData = null;
+			this.tableConditions.RemoveCaption = "Remove";
+			this.tableConditions.RowHeaderWidth = 0F;
+			this.tableConditions.RunInitialAddEvents = true;
+			this.tableConditions.Size = new System.Drawing.Size(875, 320);
+			this.tableConditions.Sorted = false;
+			this.tableConditions.TabIndex = 20;
+			this.tableConditions.UndoManager = null;
+			this.tableConditions.UseAutoComplete = true;
+			// 
+			// label4
+			// 
+			this.label4.AutoSize = true;
+			this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label4.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label4.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label4.Location = new System.Drawing.Point(6, 165);
+			this.label4.Name = "label4";
+			this.label4.Size = new System.Drawing.Size(52, 13);
+			this.label4.TabIndex = 8;
+			this.label4.Text = "Case tag:";
+			// 
+			// cboTag
+			// 
+			this.cboTag.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboTag.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboTag.BackColor = System.Drawing.Color.White;
+			this.cboTag.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboTag.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboTag.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboTag.FormattingEnabled = true;
+			this.cboTag.Location = new System.Drawing.Point(82, 162);
+			this.cboTag.Name = "cboTag";
+			this.cboTag.SelectedIndex = -1;
+			this.cboTag.SelectedItem = null;
+			this.cboTag.Size = new System.Drawing.Size(270, 21);
+			this.cboTag.Sorted = false;
+			this.cboTag.TabIndex = 6;
+			// 
+			// label5
+			// 
+			this.label5.AutoSize = true;
+			this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label5.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label5.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label5.Location = new System.Drawing.Point(490, 28);
+			this.label5.Name = "label5";
+			this.label5.Size = new System.Drawing.Size(55, 13);
+			this.label5.TabIndex = 10;
+			this.label5.Text = "File name:";
+			// 
+			// txtFile
+			// 
+			this.txtFile.BackColor = System.Drawing.Color.White;
+			this.txtFile.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtFile.ForeColor = System.Drawing.Color.Black;
+			this.txtFile.Location = new System.Drawing.Point(551, 25);
+			this.txtFile.Name = "txtFile";
+			this.txtFile.Size = new System.Drawing.Size(220, 20);
+			this.txtFile.TabIndex = 2;
+			// 
+			// label6
+			// 
+			this.label6.AutoSize = true;
+			this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label6.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label6.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label6.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label6.Location = new System.Drawing.Point(6, 54);
+			this.label6.Name = "label6";
+			this.label6.Size = new System.Drawing.Size(39, 13);
+			this.label6.TabIndex = 21;
+			this.label6.Text = "Group:";
+			// 
+			// txtGroup
+			// 
+			this.txtGroup.BackColor = System.Drawing.Color.White;
+			this.txtGroup.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtGroup.ForeColor = System.Drawing.Color.Black;
+			this.txtGroup.Location = new System.Drawing.Point(82, 51);
+			this.txtGroup.Name = "txtGroup";
+			this.txtGroup.Size = new System.Drawing.Size(402, 20);
+			this.txtGroup.TabIndex = 4;
+			// 
+			// skinnedGroupBox1
+			// 
+			this.skinnedGroupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedGroupBox1.Controls.Add(this.tableConditions);
+			this.skinnedGroupBox1.Location = new System.Drawing.Point(3, 199);
+			this.skinnedGroupBox1.Name = "skinnedGroupBox1";
+			this.skinnedGroupBox1.Size = new System.Drawing.Size(887, 349);
+			this.skinnedGroupBox1.TabIndex = 22;
+			this.skinnedGroupBox1.TabStop = false;
+			this.skinnedGroupBox1.Text = "Conditions";
+			// 
+			// skinnedGroupBox2
+			// 
+			this.skinnedGroupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedGroupBox2.Controls.Add(this.skinnedLabel1);
+			this.skinnedGroupBox2.Controls.Add(this.txtLabel);
+			this.skinnedGroupBox2.Controls.Add(this.txtName);
+			this.skinnedGroupBox2.Controls.Add(this.label1);
+			this.skinnedGroupBox2.Controls.Add(this.label4);
+			this.skinnedGroupBox2.Controls.Add(this.cboTag);
+			this.skinnedGroupBox2.Controls.Add(this.txtGroup);
+			this.skinnedGroupBox2.Controls.Add(this.cmdOpen);
+			this.skinnedGroupBox2.Controls.Add(this.label2);
+			this.skinnedGroupBox2.Controls.Add(this.txtDescription);
+			this.skinnedGroupBox2.Controls.Add(this.label6);
+			this.skinnedGroupBox2.Controls.Add(this.label5);
+			this.skinnedGroupBox2.Controls.Add(this.txtFile);
+			this.skinnedGroupBox2.Location = new System.Drawing.Point(3, 3);
+			this.skinnedGroupBox2.Name = "skinnedGroupBox2";
+			this.skinnedGroupBox2.Size = new System.Drawing.Size(887, 190);
+			this.skinnedGroupBox2.TabIndex = 23;
+			this.skinnedGroupBox2.TabStop = false;
+			this.skinnedGroupBox2.Text = "Information";
+			// 
+			// skinnedGroupBox3
+			// 
+			this.skinnedGroupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedGroupBox3.Controls.Add(this.gridLines);
+			this.skinnedGroupBox3.Location = new System.Drawing.Point(3, 554);
+			this.skinnedGroupBox3.Name = "skinnedGroupBox3";
+			this.skinnedGroupBox3.Size = new System.Drawing.Size(887, 124);
+			this.skinnedGroupBox3.TabIndex = 24;
+			this.skinnedGroupBox3.TabStop = false;
+			this.skinnedGroupBox3.Text = "Lines";
+			// 
+			// gridLines
+			// 
+			this.gridLines.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridLines.Location = new System.Drawing.Point(6, 23);
+			this.gridLines.Name = "gridLines";
+			this.gridLines.ReadOnly = false;
+			this.gridLines.Size = new System.Drawing.Size(875, 95);
+			this.gridLines.TabIndex = 0;
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.AutoSize = true;
+			this.skinnedLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel1.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel1.Location = new System.Drawing.Point(490, 54);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(36, 13);
+			this.skinnedLabel1.TabIndex = 23;
+			this.skinnedLabel1.Text = "Label:";
+			// 
+			// txtLabel
+			// 
+			this.txtLabel.BackColor = System.Drawing.Color.White;
+			this.txtLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtLabel.ForeColor = System.Drawing.Color.Black;
+			this.txtLabel.Location = new System.Drawing.Point(551, 51);
+			this.txtLabel.Name = "txtLabel";
+			this.txtLabel.Size = new System.Drawing.Size(220, 20);
+			this.txtLabel.TabIndex = 22;
+			// 
+			// RecipeEditor
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.skinnedGroupBox3);
+			this.Controls.Add(this.skinnedGroupBox2);
+			this.Controls.Add(this.skinnedGroupBox1);
+			this.Name = "RecipeEditor";
+			this.Size = new System.Drawing.Size(893, 681);
+			this.skinnedGroupBox1.ResumeLayout(false);
+			this.skinnedGroupBox2.ResumeLayout(false);
+			this.skinnedGroupBox2.PerformLayout();
+			this.skinnedGroupBox3.ResumeLayout(false);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedTextBox txtName;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedTextBox txtDescription;
+		private Desktop.Skinning.SkinnedButton cmdOpen;
+		private Desktop.CommonControls.PropertyTable tableConditions;
+		private Desktop.Skinning.SkinnedLabel label4;
+		private Desktop.Skinning.SkinnedComboBox cboTag;
+		private Desktop.Skinning.SkinnedLabel label5;
+		private Desktop.Skinning.SkinnedTextBox txtFile;
+		private Desktop.Skinning.SkinnedLabel label6;
+		private Desktop.Skinning.SkinnedTextBox txtGroup;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox1;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox2;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox3;
+		private Controls.DialogueGrid gridLines;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
+		private Desktop.Skinning.SkinnedTextBox txtLabel;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Activities/RecipeEditor.cs b/editor source/SPNATI Character Editor/Activities/RecipeEditor.cs
new file mode 100644
index 0000000000000000000000000000000000000000..279125c244a846d88a515f30cfba193a1a7ecb45
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/RecipeEditor.cs	
@@ -0,0 +1,116 @@
+using Desktop;
+using SPNATI_Character_Editor.Controls;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Activities
+{
+	[Activity(typeof(Recipe), 0)]
+	public partial class RecipeEditor : Activity
+	{
+		private Recipe _recipe;
+
+		public RecipeEditor()
+		{
+			InitializeComponent();
+		}
+
+		protected override void OnInitialize()
+		{
+			_recipe = Record as Recipe;
+			txtName.Text = _recipe.Name;
+			txtDescription.Text = _recipe.Description;
+			txtFile.Text = _recipe.FileName;
+			txtGroup.Text = _recipe.Group;
+			txtLabel.Text = _recipe.Label;
+			cboTag.Items.AddRange(TriggerDatabase.Triggers);
+
+			for (int i = 0; i < TriggerDatabase.Triggers.Count; i++)
+			{
+				if (_recipe.Case.Tag == TriggerDatabase.Triggers[i].Tag)
+				{
+					cboTag.SelectedIndex = i;
+					break;
+				}
+			}
+
+			ReloadTable();
+			cboTag.SelectedIndexChanged += cboTag_SelectedIndexChanged;
+			gridLines.SetData(null, _recipe.Case);
+		}
+
+		private void ReloadTable()
+		{
+			tableConditions.Data = null;
+			Trigger trigger = TriggerDatabase.GetTrigger(_recipe.Case.Tag);
+			if (trigger == null || trigger.HasTarget)
+			{
+				tableConditions.RecordFilter = null;
+			}
+			else
+			{
+				tableConditions.RecordFilter = FilterTargets;
+			}
+			tableConditions.Data = _recipe.Case;
+			CaseControl.AddSpeedButtons(tableConditions, _recipe.Case.Tag);
+		}
+
+		private bool FilterTargets(IRecord record)
+		{
+			return record.Group != "Target";
+		}
+
+		public override void Save()
+		{
+			_recipe.Name = txtName.Text;
+			_recipe.Description = txtDescription.Text;
+			_recipe.FileName = txtFile.Text;
+			_recipe.Group = txtGroup.Text;
+			_recipe.Label = txtLabel.Text;
+			tableConditions.Save();
+			gridLines.Save();
+
+			string file = _recipe.GetFilePath();
+			string json = Json.Serialize(_recipe);
+			try
+			{
+				File.WriteAllText(file, json);
+			}
+			catch (Exception ex)
+			{
+				MessageBox.Show("Failed to save recipe: " + ex.Message);
+			}
+		}
+
+		private void cboTag_SelectedIndexChanged(object sender, System.EventArgs e)
+		{
+			Trigger trigger = cboTag.SelectedItem as Trigger;
+			if (_recipe.Case.Tag == trigger.Tag) { return; }
+			tableConditions.Save();
+			_recipe.Case.Tag = trigger.Tag;
+			ReloadTable();
+		}
+
+		private void cmdOpen_Click(object sender, System.EventArgs e)
+		{
+			Save();
+			string directory = Path.GetDirectoryName(_recipe.GetFilePath());
+			try
+			{
+				ProcessStartInfo startInfo = new ProcessStartInfo()
+				{
+					Arguments = directory,
+					FileName = "explorer.exe"
+				};
+
+				Process.Start(startInfo);
+			}
+			catch (Exception ex)
+			{
+				MessageBox.Show(ex.Message);
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterTagControl.resx b/editor source/SPNATI Character Editor/Activities/RecipeEditor.resx
similarity index 100%
rename from editor source/SPNATI Character Editor/Controls/EditControls/CharacterTagControl.resx
rename to editor source/SPNATI Character Editor/Activities/RecipeEditor.resx
diff --git a/editor source/SPNATI Character Editor/Forms/DialogueResponder.Designer.cs b/editor source/SPNATI Character Editor/Activities/RespondToThis.Designer.cs
similarity index 60%
rename from editor source/SPNATI Character Editor/Forms/DialogueResponder.Designer.cs
rename to editor source/SPNATI Character Editor/Activities/RespondToThis.Designer.cs
index c1e6e08311a6f5cb2f0cbd3cd56c681e4ab99419..6a45249f16ba62e03a5b7f524fee3c6741f36c8d 100644
--- a/editor source/SPNATI Character Editor/Forms/DialogueResponder.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/RespondToThis.Designer.cs	
@@ -1,13 +1,13 @@
-namespace SPNATI_Character_Editor.Forms
+namespace SPNATI_Character_Editor.Activities
 {
-	partial class DialogueResponder
+	partial class RespondToThis
 	{
-		/// <summary>
+		/// <summary> 
 		/// Required designer variable.
 		/// </summary>
 		private System.ComponentModel.IContainer components = null;
 
-		/// <summary>
+		/// <summary> 
 		/// Clean up any resources being used.
 		/// </summary>
 		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
@@ -20,26 +20,27 @@
 			base.Dispose(disposing);
 		}
 
-		#region Windows Form Designer generated code
+		#region Component Designer generated code
 
-		/// <summary>
-		/// Required method for Designer support - do not modify
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
 		/// the contents of this method with the code editor.
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
+			this.lblFrom = new Desktop.Skinning.SkinnedLabel();
 			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
 			this.splitContainer2 = new System.Windows.Forms.SplitContainer();
 			this.gridSource = new SPNATI_Character_Editor.Controls.DialogueGrid();
 			this.imgSource = new SPNATI_Character_Editor.Controls.CharacterImageBox();
 			this.splitContainer3 = new System.Windows.Forms.SplitContainer();
 			this.responseControl = new SPNATI_Character_Editor.Controls.CaseControl();
-			this.cmdJumpToDialogue = new System.Windows.Forms.Button();
-			this.cmdAccept = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.lblResponse = new System.Windows.Forms.Label();
+			this.lblResponse = new Desktop.Skinning.SkinnedLabel();
 			this.imgResponse = new SPNATI_Character_Editor.Controls.CharacterImageBox();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.cmdAccept = new Desktop.Skinning.SkinnedButton();
+			this.cmdJumpToDialogue = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
 			this.splitContainer1.Panel1.SuspendLayout();
 			this.splitContainer1.Panel2.SuspendLayout();
@@ -52,21 +53,28 @@
 			this.splitContainer3.Panel1.SuspendLayout();
 			this.splitContainer3.Panel2.SuspendLayout();
 			this.splitContainer3.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
-			// label1
+			// lblFrom
 			// 
-			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(3, 3);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(79, 13);
-			this.label1.TabIndex = 1;
-			this.label1.Text = "Responding to:";
+			this.lblFrom.AutoSize = true;
+			this.lblFrom.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblFrom.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblFrom.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblFrom.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblFrom.Location = new System.Drawing.Point(3, -1);
+			this.lblFrom.Name = "lblFrom";
+			this.lblFrom.Size = new System.Drawing.Size(79, 13);
+			this.lblFrom.TabIndex = 1;
+			this.lblFrom.Text = "Responding to:";
 			// 
 			// splitContainer1
 			// 
-			this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
-			this.splitContainer1.Location = new System.Drawing.Point(0, 0);
+			this.splitContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.splitContainer1.Location = new System.Drawing.Point(1, 9);
 			this.splitContainer1.Name = "splitContainer1";
 			this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
 			// 
@@ -77,9 +85,9 @@
 			// splitContainer1.Panel2
 			// 
 			this.splitContainer1.Panel2.Controls.Add(this.splitContainer3);
-			this.splitContainer1.Size = new System.Drawing.Size(1254, 720);
-			this.splitContainer1.SplitterDistance = 154;
-			this.splitContainer1.TabIndex = 2;
+			this.splitContainer1.Size = new System.Drawing.Size(1252, 676);
+			this.splitContainer1.SplitterDistance = 144;
+			this.splitContainer1.TabIndex = 14;
 			// 
 			// splitContainer2
 			// 
@@ -90,13 +98,13 @@
 			// splitContainer2.Panel1
 			// 
 			this.splitContainer2.Panel1.Controls.Add(this.gridSource);
-			this.splitContainer2.Panel1.Controls.Add(this.label1);
+			this.splitContainer2.Panel1.Controls.Add(this.lblFrom);
 			// 
 			// splitContainer2.Panel2
 			// 
 			this.splitContainer2.Panel2.Controls.Add(this.imgSource);
-			this.splitContainer2.Size = new System.Drawing.Size(1254, 154);
-			this.splitContainer2.SplitterDistance = 936;
+			this.splitContainer2.Size = new System.Drawing.Size(1252, 144);
+			this.splitContainer2.SplitterDistance = 934;
 			this.splitContainer2.TabIndex = 3;
 			// 
 			// gridSource
@@ -104,10 +112,10 @@
 			this.gridSource.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.gridSource.Location = new System.Drawing.Point(2, 19);
+			this.gridSource.Location = new System.Drawing.Point(2, 15);
 			this.gridSource.Name = "gridSource";
 			this.gridSource.ReadOnly = true;
-			this.gridSource.Size = new System.Drawing.Size(930, 132);
+			this.gridSource.Size = new System.Drawing.Size(928, 122);
 			this.gridSource.TabIndex = 0;
 			this.gridSource.HighlightRow += new System.EventHandler<int>(this.gridSource_HighlightRow);
 			// 
@@ -117,7 +125,7 @@
 			this.imgSource.Location = new System.Drawing.Point(0, 0);
 			this.imgSource.Name = "imgSource";
 			this.imgSource.ShowTextBox = false;
-			this.imgSource.Size = new System.Drawing.Size(314, 154);
+			this.imgSource.Size = new System.Drawing.Size(314, 144);
 			this.imgSource.TabIndex = 0;
 			// 
 			// splitContainer3
@@ -129,16 +137,13 @@
 			// splitContainer3.Panel1
 			// 
 			this.splitContainer3.Panel1.Controls.Add(this.responseControl);
-			this.splitContainer3.Panel1.Controls.Add(this.cmdJumpToDialogue);
 			this.splitContainer3.Panel1.Controls.Add(this.lblResponse);
 			// 
 			// splitContainer3.Panel2
 			// 
 			this.splitContainer3.Panel2.Controls.Add(this.imgResponse);
-			this.splitContainer3.Panel2.Controls.Add(this.cmdCancel);
-			this.splitContainer3.Panel2.Controls.Add(this.cmdAccept);
-			this.splitContainer3.Size = new System.Drawing.Size(1254, 562);
-			this.splitContainer3.SplitterDistance = 935;
+			this.splitContainer3.Size = new System.Drawing.Size(1252, 528);
+			this.splitContainer3.SplitterDistance = 933;
 			this.splitContainer3.TabIndex = 0;
 			// 
 			// responseControl
@@ -146,37 +151,43 @@
 			this.responseControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.responseControl.Location = new System.Drawing.Point(3, 16);
+			this.responseControl.Location = new System.Drawing.Point(3, 24);
 			this.responseControl.Name = "responseControl";
-			this.responseControl.Size = new System.Drawing.Size(930, 505);
+			this.responseControl.Size = new System.Drawing.Size(928, 497);
 			this.responseControl.TabIndex = 13;
+			this.responseControl.HighlightRow += new System.EventHandler<int>(this.ResponseControl_HighlightRow);
 			// 
-			// cmdJumpToDialogue
+			// lblResponse
 			// 
-			this.cmdJumpToDialogue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdJumpToDialogue.Location = new System.Drawing.Point(804, 527);
-			this.cmdJumpToDialogue.Name = "cmdJumpToDialogue";
-			this.cmdJumpToDialogue.Size = new System.Drawing.Size(128, 23);
-			this.cmdJumpToDialogue.TabIndex = 12;
-			this.cmdJumpToDialogue.Text = "Edit in Dialogue Editor";
-			this.cmdJumpToDialogue.UseVisualStyleBackColor = true;
-			this.cmdJumpToDialogue.Click += new System.EventHandler(this.cmdJumpToDialogue_Click);
+			this.lblResponse.AutoSize = true;
+			this.lblResponse.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblResponse.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblResponse.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblResponse.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblResponse.Location = new System.Drawing.Point(3, 5);
+			this.lblResponse.Name = "lblResponse";
+			this.lblResponse.Size = new System.Drawing.Size(91, 13);
+			this.lblResponse.TabIndex = 2;
+			this.lblResponse.Text = "Response from X:";
 			// 
-			// cmdAccept
+			// imgResponse
 			// 
-			this.cmdAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdAccept.Location = new System.Drawing.Point(41, 527);
-			this.cmdAccept.Name = "cmdAccept";
-			this.cmdAccept.Size = new System.Drawing.Size(128, 23);
-			this.cmdAccept.TabIndex = 11;
-			this.cmdAccept.Text = "Accept";
-			this.cmdAccept.UseVisualStyleBackColor = true;
-			this.cmdAccept.Click += new System.EventHandler(this.cmdAccept_Click);
+			this.imgResponse.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.imgResponse.Location = new System.Drawing.Point(0, 0);
+			this.imgResponse.Name = "imgResponse";
+			this.imgResponse.ShowTextBox = false;
+			this.imgResponse.Size = new System.Drawing.Size(315, 528);
+			this.imgResponse.TabIndex = 0;
 			// 
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdCancel.Location = new System.Drawing.Point(175, 527);
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(1123, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(128, 23);
 			this.cmdCancel.TabIndex = 10;
@@ -184,37 +195,55 @@
 			this.cmdCancel.UseVisualStyleBackColor = true;
 			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
 			// 
-			// lblResponse
+			// cmdAccept
 			// 
-			this.lblResponse.AutoSize = true;
-			this.lblResponse.Location = new System.Drawing.Point(3, 0);
-			this.lblResponse.Name = "lblResponse";
-			this.lblResponse.Size = new System.Drawing.Size(91, 13);
-			this.lblResponse.TabIndex = 2;
-			this.lblResponse.Text = "Responds from X:";
+			this.cmdAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdAccept.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdAccept.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdAccept.Flat = false;
+			this.cmdAccept.Location = new System.Drawing.Point(989, 4);
+			this.cmdAccept.Name = "cmdAccept";
+			this.cmdAccept.Size = new System.Drawing.Size(128, 23);
+			this.cmdAccept.TabIndex = 11;
+			this.cmdAccept.Text = "Finish";
+			this.cmdAccept.UseVisualStyleBackColor = true;
+			this.cmdAccept.Click += new System.EventHandler(this.cmdAccept_Click);
 			// 
-			// imgResponse
+			// cmdJumpToDialogue
 			// 
-			this.imgResponse.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.imgResponse.Location = new System.Drawing.Point(0, 0);
-			this.imgResponse.Name = "imgResponse";
-			this.imgResponse.ShowTextBox = false;
-			this.imgResponse.Size = new System.Drawing.Size(315, 521);
-			this.imgResponse.TabIndex = 0;
+			this.cmdJumpToDialogue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdJumpToDialogue.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdJumpToDialogue.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdJumpToDialogue.Flat = false;
+			this.cmdJumpToDialogue.Location = new System.Drawing.Point(855, 4);
+			this.cmdJumpToDialogue.Name = "cmdJumpToDialogue";
+			this.cmdJumpToDialogue.Size = new System.Drawing.Size(128, 23);
+			this.cmdJumpToDialogue.TabIndex = 12;
+			this.cmdJumpToDialogue.Text = "Edit Full Screen";
+			this.cmdJumpToDialogue.UseVisualStyleBackColor = true;
+			this.cmdJumpToDialogue.Click += new System.EventHandler(this.cmdJumpToDialogue_Click);
+			// 
+			// skinnedPanel1
 			// 
-			// DialogueResponder
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdAccept);
+			this.skinnedPanel1.Controls.Add(this.cmdJumpToDialogue);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 685);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(1254, 30);
+			this.skinnedPanel1.TabIndex = 15;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// RespondToThis
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.ClientSize = new System.Drawing.Size(1254, 720);
-			this.ControlBox = false;
 			this.Controls.Add(this.splitContainer1);
-			this.Name = "DialogueResponder";
-			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
-			this.Text = "Respond to This";
-			this.Shown += new System.EventHandler(this.DialogueResponder_Shown);
+			this.Controls.Add(this.skinnedPanel1);
+			this.Name = "RespondToThis";
+			this.Size = new System.Drawing.Size(1254, 715);
 			this.splitContainer1.Panel1.ResumeLayout(false);
 			this.splitContainer1.Panel2.ResumeLayout(false);
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
@@ -229,23 +258,25 @@
 			this.splitContainer3.Panel2.ResumeLayout(false);
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit();
 			this.splitContainer3.ResumeLayout(false);
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 
 		}
 
 		#endregion
 
-		private Controls.DialogueGrid gridSource;
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedLabel lblFrom;
 		private System.Windows.Forms.SplitContainer splitContainer1;
 		private System.Windows.Forms.SplitContainer splitContainer2;
-		private System.Windows.Forms.SplitContainer splitContainer3;
-		private System.Windows.Forms.Label lblResponse;
+		private Controls.DialogueGrid gridSource;
 		private Controls.CharacterImageBox imgSource;
-		private Controls.CharacterImageBox imgResponse;
-		private System.Windows.Forms.Button cmdJumpToDialogue;
-		private System.Windows.Forms.Button cmdAccept;
-		private System.Windows.Forms.Button cmdCancel;
+		private System.Windows.Forms.SplitContainer splitContainer3;
 		private Controls.CaseControl responseControl;
+		private Desktop.Skinning.SkinnedLabel lblResponse;
+		private Controls.CharacterImageBox imgResponse;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedButton cmdAccept;
+		private Desktop.Skinning.SkinnedButton cmdJumpToDialogue;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
-}
\ No newline at end of file
+}
diff --git a/editor source/SPNATI Character Editor/Activities/RespondToThis.cs b/editor source/SPNATI Character Editor/Activities/RespondToThis.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a93fb3dc8c2355795f5b40200e2fcb696b82fd1c
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/RespondToThis.cs	
@@ -0,0 +1,170 @@
+using Desktop;
+using SPNATI_Character_Editor.Forms;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Activities
+{
+	[Activity(typeof(ResponseRecord), 0)]
+	public partial class RespondToThis : Activity
+	{
+		public RespondToThis()
+		{
+			InitializeComponent();
+		}
+
+		private bool _isNew;
+		private Case _sourceCase;
+		private Case _responseCase;
+		private Character _source;
+		private Character _responder;
+
+		private bool _closing;
+		private bool _jumpingToWorkspace;
+		private bool _cancelling;
+
+		protected override void OnInitialize()
+		{
+			ResponseRecord rec = Record as ResponseRecord;
+			_isNew = rec.IsNew;
+			lblFrom.Text = $"Responding to {rec.SourceCharacter}:";
+			lblResponse.Text = "Response from " + rec.ResponseCharacter;
+			_source = rec.SourceCharacter;
+			_sourceCase = rec.SourceCase;
+			_responder = rec.ResponseCharacter;
+			_responseCase = rec.ResponseCase;
+			gridSource.SetData(_source, _sourceCase);
+			responseControl.SetCharacter(_responder);
+			responseControl.SetCase(new Stage(_responseCase.Stages[0]), _responseCase);
+		}
+
+		protected override void OnFirstActivate()
+		{
+			responseControl.Focus();
+		}
+
+		private void cmdJumpToDialogue_Click(object sender, System.EventArgs e)
+		{
+			_jumpingToWorkspace = true;
+			_closing = true;
+			Shell.Instance.CloseWorkspace(Workspace);
+		}
+
+		private void cmdAccept_Click(object sender, System.EventArgs e)
+		{
+			_closing = true;
+			Shell.Instance.CloseWorkspace(Workspace);
+		}
+
+		private void cmdCancel_Click(object sender, System.EventArgs e)
+		{
+			_cancelling = true;
+			_closing = true;
+			Shell.Instance.CloseWorkspace(Workspace);
+		}
+
+		private void gridSource_HighlightRow(object sender, int index)
+		{
+			if (index == -1)
+				return;
+			string image = gridSource.GetImage(index);
+			ImageLibrary lib = ImageLibrary.Get(_source);
+			CharacterImage img = null;
+			img = lib.Find(image);
+			if (img == null)
+			{
+				int stage = _sourceCase.Stages[0];
+				image = DialogueLine.GetStageImage(stage, DialogueLine.GetDefaultImage(image));
+				img = lib.Find(image);
+			}
+			imgSource.SetImage(img);
+		}
+
+
+		private void ResponseControl_HighlightRow(object sender, int line)
+		{
+			if (line == -1)
+				return;
+			string image = responseControl.GetImage(line);
+			ImageLibrary lib = ImageLibrary.Get(_responder);
+			CharacterImage img = null;
+			img = lib.Find(image);
+			if (img == null)
+			{
+				int stage = _responseCase.Stages[0];
+				image = DialogueLine.GetStageImage(stage, DialogueLine.GetDefaultImage(image));
+				img = lib.Find(image);
+			}
+			imgResponse.SetImage(img);
+		}
+
+		public override bool CanDeactivate(DeactivateArgs args)
+		{
+			if (_closing) { return true; }
+			DiscardResponsePrompt prompt = new DiscardResponsePrompt();
+			DialogResult result = prompt.ShowDialog();
+			if (result == DialogResult.Cancel)
+			{
+				return false;
+			}
+			if (result == DialogResult.No)
+			{
+				_cancelling = true;
+			}
+			_closing = true;
+			Shell.Instance.CloseWorkspace(Workspace);
+			return true;
+		}
+
+
+		public override void Quit()
+		{
+			if (_cancelling) { return; }
+			responseControl.Save();
+			if (_isNew)
+			{
+				CharacterEditorData responderData = CharacterDatabase.GetEditorData(_responder);
+				responderData?.MarkResponse(_source, _sourceCase, _responseCase);
+				_responder.Behavior.AddWorkingCase(_responseCase);
+			}
+
+			if (_jumpingToWorkspace)
+			{
+				Shell.Instance.Launch<Character, DialogueEditor>(_responder, _responseCase);
+			}
+			else
+			{
+				//if their workspace isn't open, save them now
+				IWorkspace ws = Shell.Instance.GetWorkspace(_responder);
+				if (ws == null)
+				{
+					Serialization.ExportCharacter(_responder);
+				}
+			}
+		}
+	}
+
+	public class ResponseRecord : BasicRecord
+	{
+		public Character SourceCharacter;
+		public Case SourceCase;
+		public Character ResponseCharacter;
+		public Case ResponseCase;
+		public bool IsNew;
+
+		public ResponseRecord(Character source, Case sourceCase, Character responder, Case response, bool isNew)
+		{
+			Key = "Response";
+			SourceCharacter = source;
+			SourceCase = sourceCase;
+			ResponseCharacter = responder;
+			ResponseCase = response;
+			IsNew = isNew;
+			Name = $"{ResponseCharacter.FirstName}->{SourceCharacter.FirstName}";
+		}
+
+		public override string ToString()
+		{
+			return Name;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleControl.resx b/editor source/SPNATI Character Editor/Activities/RespondToThis.resx
similarity index 100%
rename from editor source/SPNATI Character Editor/Controls/EditControls/CollectibleControl.resx
rename to editor source/SPNATI Character Editor/Activities/RespondToThis.resx
diff --git a/editor source/SPNATI Character Editor/Activities/ScreenshotTaker.Designer.cs b/editor source/SPNATI Character Editor/Activities/ScreenshotTaker.Designer.cs
index 8f82a463159b5083454a4695e402fa53bfbfa72e..1b8a4312c3457deabcd0024170a17ae7634c0557 100644
--- a/editor source/SPNATI Character Editor/Activities/ScreenshotTaker.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/ScreenshotTaker.Designer.cs	
@@ -28,16 +28,18 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.txtName = new System.Windows.Forms.TextBox();
-			this.cmdImport = new System.Windows.Forms.Button();
-			this.label2 = new System.Windows.Forms.Label();
-			this.cmdAdvanced = new System.Windows.Forms.Button();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.txtName = new Desktop.Skinning.SkinnedTextBox();
+			this.cmdImport = new Desktop.Skinning.SkinnedButton();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdAdvanced = new Desktop.Skinning.SkinnedButton();
 			this.SuspendLayout();
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(3, 6);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(55, 13);
@@ -46,6 +48,9 @@
 			// 
 			// txtName
 			// 
+			this.txtName.BackColor = System.Drawing.Color.White;
+			this.txtName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtName.ForeColor = System.Drawing.Color.Black;
 			this.txtName.Location = new System.Drawing.Point(64, 3);
 			this.txtName.Name = "txtName";
 			this.txtName.Size = new System.Drawing.Size(174, 20);
@@ -53,6 +58,9 @@
 			// 
 			// cmdImport
 			// 
+			this.cmdImport.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdImport.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdImport.Flat = false;
 			this.cmdImport.Font = new System.Drawing.Font("Microsoft Sans Serif", 16F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
 			this.cmdImport.Location = new System.Drawing.Point(6, 29);
 			this.cmdImport.Name = "cmdImport";
@@ -66,6 +74,8 @@
 			// 
 			this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label2.Location = new System.Drawing.Point(6, 102);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(903, 169);
@@ -75,9 +85,12 @@
 			// 
 			// cmdAdvanced
 			// 
+			this.cmdAdvanced.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdAdvanced.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdAdvanced.Flat = false;
 			this.cmdAdvanced.Location = new System.Drawing.Point(244, 29);
 			this.cmdAdvanced.Name = "cmdAdvanced";
-			this.cmdAdvanced.Size = new System.Drawing.Size(148, 23);
+			this.cmdAdvanced.Size = new System.Drawing.Size(194, 23);
 			this.cmdAdvanced.TabIndex = 6;
 			this.cmdAdvanced.Text = "Set Part Transparencies...";
 			this.cmdAdvanced.UseVisualStyleBackColor = true;
@@ -101,10 +114,10 @@
 
 		#endregion
 
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.TextBox txtName;
-		private System.Windows.Forms.Button cmdImport;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.Button cmdAdvanced;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedTextBox txtName;
+		private Desktop.Skinning.SkinnedButton cmdImport;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedButton cmdAdvanced;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/ScreenshotTaker.cs b/editor source/SPNATI Character Editor/Activities/ScreenshotTaker.cs
index 25251411e40595b39be2328dd811e202ab0e91ee..a49b8a7bd76f93a75b476e88dac4a272031e193a 100644
--- a/editor source/SPNATI Character Editor/Activities/ScreenshotTaker.cs	
+++ b/editor source/SPNATI Character Editor/Activities/ScreenshotTaker.cs	
@@ -9,9 +9,10 @@ using System.Windows.Forms;
 namespace SPNATI_Character_Editor.Activities
 {
 	[Activity(typeof(Character), 215)]
+	[Activity(typeof(Costume), 215)]
 	public partial class ScreenshotTaker : Activity
 	{
-		private Character _character;
+		private ISkin _character;
 		private Dictionary<string, string> _extraData = new Dictionary<string, string>();
 
 		public ScreenshotTaker()
@@ -26,7 +27,7 @@ namespace SPNATI_Character_Editor.Activities
 
 		protected override void OnInitialize()
 		{
-			_character = Record as Character;
+			_character = Record as ISkin;
 		}
 
 		private void cmdImport_Click(object sender, EventArgs e)
diff --git a/editor source/SPNATI Character Editor/Activities/SituationEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/SituationEditor.Designer.cs
index af9bb618e17330e4e41edfc9b3ad1fd7013c62a1..9aa3e97b54579938ac99a786620a68cce5052b4f 100644
--- a/editor source/SPNATI Character Editor/Activities/SituationEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/SituationEditor.Designer.cs	
@@ -28,13 +28,18 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.gridCases = new System.Windows.Forms.DataGridView();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.gridCases = new Desktop.Skinning.SkinnedDataGridView();
+			this.gridLines = new SPNATI_Character_Editor.Controls.DialogueGrid();
 			this.ColName = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColDescription = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.ColPriority = new Desktop.Skinning.SkinnedDataGridViewComboBoxColumn();
 			this.ColStages = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColTrigger = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColJump = new System.Windows.Forms.DataGridViewButtonColumn();
-			this.gridLines = new SPNATI_Character_Editor.Controls.DialogueGrid();
+			this.ColJump = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
+			this.ColDelete = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
 			((System.ComponentModel.ISupportInitialize)(this.gridCases)).BeginInit();
 			this.SuspendLayout();
 			// 
@@ -44,16 +49,50 @@
 			this.gridCases.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridCases.BackgroundColor = System.Drawing.Color.White;
+			this.gridCases.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridCases.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridCases.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
 			this.gridCases.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridCases.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColName,
             this.ColDescription,
+            this.ColPriority,
             this.ColStages,
             this.ColTrigger,
-            this.ColJump});
+            this.ColJump,
+            this.ColDelete});
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle2.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridCases.DefaultCellStyle = dataGridViewCellStyle2;
+			this.gridCases.EnableHeadersVisualStyles = false;
+			this.gridCases.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridCases.GridColor = System.Drawing.Color.LightGray;
 			this.gridCases.Location = new System.Drawing.Point(0, 0);
 			this.gridCases.MultiSelect = false;
 			this.gridCases.Name = "gridCases";
+			this.gridCases.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle3.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridCases.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
 			this.gridCases.Size = new System.Drawing.Size(999, 458);
 			this.gridCases.TabIndex = 0;
 			this.gridCases.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridCases_CellContentClick);
@@ -61,6 +100,17 @@
 			this.gridCases.SelectionChanged += new System.EventHandler(this.gridCases_SelectionChanged);
 			this.gridCases.UserDeletingRow += new System.Windows.Forms.DataGridViewRowCancelEventHandler(this.gridCases_UserDeletingRow);
 			// 
+			// gridLines
+			// 
+			this.gridLines.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridLines.Location = new System.Drawing.Point(0, 462);
+			this.gridLines.Name = "gridLines";
+			this.gridLines.ReadOnly = false;
+			this.gridLines.Size = new System.Drawing.Size(999, 169);
+			this.gridLines.TabIndex = 1;
+			this.gridLines.HighlightRow += new System.EventHandler<int>(this.gridLines_HighlightRow);
+			// 
 			// ColName
 			// 
 			this.ColName.HeaderText = "Name";
@@ -73,6 +123,15 @@
 			this.ColDescription.HeaderText = "Description";
 			this.ColDescription.Name = "ColDescription";
 			// 
+			// ColPriority
+			// 
+			this.ColPriority.AutoComplete = false;
+			this.ColPriority.DisplayMember = null;
+			this.ColPriority.HeaderText = "Priority";
+			this.ColPriority.Name = "ColPriority";
+			this.ColPriority.Sorted = false;
+			this.ColPriority.Width = 90;
+			// 
 			// ColStages
 			// 
 			this.ColStages.HeaderText = "Stages";
@@ -89,22 +148,21 @@
 			// 
 			// ColJump
 			// 
+			this.ColJump.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColJump.Flat = false;
 			this.ColJump.HeaderText = "";
 			this.ColJump.Name = "ColJump";
 			this.ColJump.Resizable = System.Windows.Forms.DataGridViewTriState.True;
 			this.ColJump.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
 			this.ColJump.Width = 24;
 			// 
-			// gridLines
+			// ColDelete
 			// 
-			this.gridLines.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.gridLines.Location = new System.Drawing.Point(0, 462);
-			this.gridLines.Name = "gridLines";
-			this.gridLines.ReadOnly = false;
-			this.gridLines.Size = new System.Drawing.Size(999, 169);
-			this.gridLines.TabIndex = 1;
-			this.gridLines.HighlightRow += new System.EventHandler<int>(this.gridLines_HighlightRow);
+			this.ColDelete.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColDelete.Flat = false;
+			this.ColDelete.HeaderText = "";
+			this.ColDelete.Name = "ColDelete";
+			this.ColDelete.Width = 21;
 			// 
 			// SituationEditor
 			// 
@@ -121,12 +179,14 @@
 
 		#endregion
 
-		private System.Windows.Forms.DataGridView gridCases;
+		private Desktop.Skinning.SkinnedDataGridView gridCases;
 		private Controls.DialogueGrid gridLines;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColName;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColDescription;
+		private Desktop.Skinning.SkinnedDataGridViewComboBoxColumn ColPriority;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColStages;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColTrigger;
-		private System.Windows.Forms.DataGridViewButtonColumn ColJump;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColJump;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColDelete;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/SituationEditor.cs b/editor source/SPNATI Character Editor/Activities/SituationEditor.cs
index ba3e0571799e799eba9314cd58593b2eb41ca10a..00112d8d015ba68826592a2fa830d3bd700d14d6 100644
--- a/editor source/SPNATI Character Editor/Activities/SituationEditor.cs	
+++ b/editor source/SPNATI Character Editor/Activities/SituationEditor.cs	
@@ -1,4 +1,5 @@
 using Desktop;
+using System;
 using System.Collections.Generic;
 using System.Drawing;
 using System.Windows.Forms;
@@ -13,10 +14,26 @@ namespace SPNATI_Character_Editor.Activities
 		private ImageLibrary _imageLibrary;
 		private Situation _selectedCase;
 
+		private static Dictionary<SituationPriority, string> _priorities;
+		private static Dictionary<string, SituationPriority> _prioritiesIndex;
+
+		static SituationEditor()
+		{
+			_priorities = new Dictionary<SituationPriority, string>();
+			_prioritiesIndex = new Dictionary<string, SituationPriority>();
+			_priorities.Add(SituationPriority.MustTarget, "Must Target");
+			_priorities.Add(SituationPriority.Noteworthy, "Noteworthy");
+			_priorities.Add(SituationPriority.FYI, "FYI");
+			foreach (KeyValuePair<SituationPriority, string> kvp in _priorities)
+			{
+				_prioritiesIndex[kvp.Value] = kvp.Key;
+			}
+		}
+
 		public SituationEditor()
 		{
 			InitializeComponent();
-
+			ColJump.Flat = ColDelete.Flat = true;
 			gridLines.ReadOnly = true;
 		}
 
@@ -29,6 +46,14 @@ namespace SPNATI_Character_Editor.Activities
 		{
 			_character = Record as Character;
 			_editorData = CharacterDatabase.GetEditorData(_character);
+
+			foreach(SituationPriority priority in Enum.GetValues(typeof(SituationPriority)))
+			{
+				if (_priorities.ContainsKey(priority))
+				{
+					ColPriority.Items.Add(_priorities[priority]);
+				}
+			}
 		}
 
 		protected override void OnFirstActivate()
@@ -80,7 +105,17 @@ namespace SPNATI_Character_Editor.Activities
 
 		private DataGridViewRow BuildLine(Situation line)
 		{
-			DataGridViewRow row = gridCases.Rows[gridCases.Rows.Add(line.Name, line.Description, line.GetStageString(), line.LinkedCase.ToString())];
+			DataGridViewRow row = gridCases.Rows[gridCases.Rows.Add(line.Name, line.Description, "", line.GetStageString(), line.LinkedCase.ToString())];
+			SituationPriority priority = line.Priority;
+			if (priority == SituationPriority.None)
+			{
+				priority = SituationPriority.Noteworthy;
+			}
+			string value;
+			if (_priorities.TryGetValue(priority, out value))
+			{
+				row.Cells[nameof(ColPriority)].Value = value;
+			}
 			DataGridViewCell jumpButton = row.Cells["ColJump"];
 			if (line.Id == 0)
 			{
@@ -114,6 +149,12 @@ namespace SPNATI_Character_Editor.Activities
 				{
 					line.Name = row.Cells["ColName"].Value?.ToString();
 					line.Description = row.Cells["ColDescription"].Value?.ToString();
+					string priority = row.Cells[ColPriority.Index].Value?.ToString();
+					SituationPriority p;
+					if (_prioritiesIndex.TryGetValue(priority, out p))
+					{
+						line.Priority = p;
+					}
 				}
 			}
 		}
@@ -165,6 +206,18 @@ namespace SPNATI_Character_Editor.Activities
 				var x = e.CellBounds.Left + (e.CellBounds.Width - w) / 2;
 				var y = e.CellBounds.Top + (e.CellBounds.Height - h) / 2;
 
+				e.Graphics.DrawImage(img, new Rectangle(x, y, w, h));
+				e.Handled = true;
+			}
+			else if (e.ColumnIndex == ColDelete.Index)
+			{
+				Image img = Properties.Resources.Delete;
+				e.Paint(e.CellBounds, DataGridViewPaintParts.All);
+				var w = img.Width;
+				var h = img.Height;
+				var x = e.CellBounds.Left + (e.CellBounds.Width - w) / 2;
+				var y = e.CellBounds.Top + (e.CellBounds.Height - h) / 2;
+
 				e.Graphics.DrawImage(img, new Rectangle(x, y, w, h));
 				e.Handled = true;
 			}
@@ -172,7 +225,11 @@ namespace SPNATI_Character_Editor.Activities
 
 		private void gridCases_CellContentClick(object sender, DataGridViewCellEventArgs e)
 		{
-			if (gridCases.Columns[e.ColumnIndex] is DataGridViewButtonColumn && e.RowIndex >= 0)
+			if (e.ColumnIndex < 0 || e.ColumnIndex >= gridCases.Columns.Count || e.RowIndex == gridCases.NewRowIndex || e.RowIndex < 0)
+			{
+				return;
+			}
+			if (e.ColumnIndex == ColJump.Index)
 			{
 				Situation situation = gridCases.Rows[e.RowIndex]?.Tag as Situation;
 				if (situation != null)
@@ -189,6 +246,16 @@ namespace SPNATI_Character_Editor.Activities
 					}
 				}
 			}
+			else if (e.ColumnIndex == ColDelete.Index)
+			{
+				DataGridViewRow row = gridCases.Rows[e.RowIndex];
+				Situation line = row.Tag as Situation;
+				if (line != null)
+				{
+					_editorData.NoteworthySituations.Remove(line);
+				}
+				gridCases.Rows.RemoveAt(e.RowIndex);
+			}
 		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/SituationEditor.resx b/editor source/SPNATI Character Editor/Activities/SituationEditor.resx
index 41fefb6e1a9fb31435a38b9a921b2e6c894563b1..2a7fe2e0793bbe4ae18e9a48d36b8f52c6b69a98 100644
--- a/editor source/SPNATI Character Editor/Activities/SituationEditor.resx	
+++ b/editor source/SPNATI Character Editor/Activities/SituationEditor.resx	
@@ -123,6 +123,9 @@
   <metadata name="ColDescription.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
+  <metadata name="ColPriority.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
   <metadata name="ColStages.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
diff --git a/editor source/SPNATI Character Editor/Activities/SkinEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/SkinEditor.Designer.cs
index fc13b52173a2d2ed533dc101a9beb47b06647dba..a4f58dd8b088d0b9d6a9ce4c25b730276e45ff38 100644
--- a/editor source/SPNATI Character Editor/Activities/SkinEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/SkinEditor.Designer.cs	
@@ -28,19 +28,21 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.txtName = new System.Windows.Forms.TextBox();
-			this.label2 = new System.Windows.Forms.Label();
-			this.cboBaseStage = new System.Windows.Forms.ComboBox();
-			this.label3 = new System.Windows.Forms.Label();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.txtName = new Desktop.Skinning.SkinnedTextBox();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.cboBaseStage = new Desktop.Skinning.SkinnedComboBox();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
 			this.gridLabels = new SPNATI_Character_Editor.Controls.StageSpecificGrid();
-			this.label22 = new System.Windows.Forms.Label();
-			this.cboDefaultPic = new System.Windows.Forms.ComboBox();
+			this.label22 = new Desktop.Skinning.SkinnedLabel();
+			this.cboDefaultPic = new Desktop.Skinning.SkinnedComboBox();
 			this.SuspendLayout();
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(3, 6);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(38, 13);
@@ -49,6 +51,9 @@
 			// 
 			// txtName
 			// 
+			this.txtName.BackColor = System.Drawing.Color.White;
+			this.txtName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtName.ForeColor = System.Drawing.Color.Black;
 			this.txtName.Location = new System.Drawing.Point(53, 3);
 			this.txtName.Name = "txtName";
 			this.txtName.Size = new System.Drawing.Size(156, 20);
@@ -57,6 +62,8 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label2.Location = new System.Drawing.Point(3, 64);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(169, 13);
@@ -65,16 +72,26 @@
 			// 
 			// cboBaseStage
 			// 
+			this.cboBaseStage.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboBaseStage.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboBaseStage.BackColor = System.Drawing.Color.White;
 			this.cboBaseStage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboBaseStage.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboBaseStage.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboBaseStage.FormattingEnabled = true;
 			this.cboBaseStage.Location = new System.Drawing.Point(178, 61);
 			this.cboBaseStage.Name = "cboBaseStage";
+			this.cboBaseStage.SelectedIndex = -1;
+			this.cboBaseStage.SelectedItem = null;
 			this.cboBaseStage.Size = new System.Drawing.Size(129, 21);
+			this.cboBaseStage.Sorted = false;
 			this.cboBaseStage.TabIndex = 3;
 			// 
 			// label3
 			// 
 			this.label3.AutoSize = true;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label3.Location = new System.Drawing.Point(3, 97);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(41, 13);
@@ -92,6 +109,8 @@
 			// label22
 			// 
 			this.label22.AutoSize = true;
+			this.label22.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label22.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label22.Location = new System.Drawing.Point(3, 32);
 			this.label22.Name = "label22";
 			this.label22.Size = new System.Drawing.Size(43, 13);
@@ -100,11 +119,19 @@
 			// 
 			// cboDefaultPic
 			// 
+			this.cboDefaultPic.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboDefaultPic.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboDefaultPic.BackColor = System.Drawing.Color.White;
 			this.cboDefaultPic.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboDefaultPic.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboDefaultPic.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboDefaultPic.FormattingEnabled = true;
 			this.cboDefaultPic.Location = new System.Drawing.Point(53, 29);
 			this.cboDefaultPic.Name = "cboDefaultPic";
+			this.cboDefaultPic.SelectedIndex = -1;
+			this.cboDefaultPic.SelectedItem = null;
 			this.cboDefaultPic.Size = new System.Drawing.Size(156, 21);
+			this.cboDefaultPic.Sorted = false;
 			this.cboDefaultPic.TabIndex = 2;
 			this.cboDefaultPic.SelectedIndexChanged += new System.EventHandler(this.cboDefaultPic_SelectedIndexChanged);
 			// 
@@ -129,13 +156,13 @@
 
 		#endregion
 
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.TextBox txtName;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.ComboBox cboBaseStage;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedTextBox txtName;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedComboBox cboBaseStage;
 		private Controls.StageSpecificGrid gridLabels;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.Label label22;
-		private System.Windows.Forms.ComboBox cboDefaultPic;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedLabel label22;
+		private Desktop.Skinning.SkinnedComboBox cboDefaultPic;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/SkinEditor.cs b/editor source/SPNATI Character Editor/Activities/SkinEditor.cs
index 00de6ddcaa22c515ed15df2594d6f210db3f28f1..a8c593ff8f80f7ee2c5b548b4761cde29580015d 100644
--- a/editor source/SPNATI Character Editor/Activities/SkinEditor.cs	
+++ b/editor source/SPNATI Character Editor/Activities/SkinEditor.cs	
@@ -35,7 +35,6 @@ namespace SPNATI_Character_Editor.Activities
 			if (!auto)
 			{
 				Save();
-				Serialization.ExportSkin(_costume);
 				if (Serialization.ExportSkin(_costume))
 				{
 					Shell.Instance.SetStatus(string.Format("{0} exported successfully at {1}.", _costume, DateTime.Now.ToShortTimeString()));
@@ -104,17 +103,16 @@ namespace SPNATI_Character_Editor.Activities
 			images.AddRange(_imageLibrary.GetImages(0));
 			if (Config.UsePrefixlessImages)
 			{
-				string prefix = Config.PrefixFilter;
 				foreach (CharacterImage img in _imageLibrary.GetImages(-1))
 				{
 					string file = img.Name;
-					if (string.IsNullOrEmpty(prefix) || !file.StartsWith(prefix))
+					if (!_imageLibrary.FilterImage(_costume.Character, file))
 					{
 						images.Add(img);
 					}
 				}
 			}
-			cboDefaultPic.DataSource = images;
+			cboDefaultPic.Items.AddRange(images);
 			_populatingImages = false;
 		}
 
diff --git a/editor source/SPNATI Character Editor/Activities/SkinTagEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/SkinTagEditor.Designer.cs
index f31d444b03a0d2cc5899bd5feb09eef3a6593c2d..dcabc862c10e3b2746235e45de9676e2d971fb4c 100644
--- a/editor source/SPNATI Character Editor/Activities/SkinTagEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/SkinTagEditor.Designer.cs	
@@ -28,64 +28,131 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
-			this.gridRemove = new System.Windows.Forms.DataGridView();
-			this.gridAdd = new System.Windows.Forms.DataGridView();
-			this.ColTagAdd = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle8 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle9 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle10 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle11 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle12 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.gridRemove = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColTag = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColRemove = new System.Windows.Forms.DataGridViewCheckBoxColumn();
+			this.ColRemove = new Desktop.Skinning.SkinnedDataGridViewCheckBoxColumn();
+			this.gridAdd = new Desktop.Skinning.SkinnedDataGridView();
+			this.ColTagAdd = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.skinnedGroupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.skinnedGroupBox2 = new Desktop.Skinning.SkinnedGroupBox();
 			((System.ComponentModel.ISupportInitialize)(this.gridRemove)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridAdd)).BeginInit();
+			this.skinnedGroupBox1.SuspendLayout();
+			this.skinnedGroupBox2.SuspendLayout();
 			this.SuspendLayout();
 			// 
-			// label1
-			// 
-			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(4, 0);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(176, 13);
-			this.label1.TabIndex = 0;
-			this.label1.Text = "Tags to Remove When Skin In Use";
-			// 
-			// label2
-			// 
-			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(368, 0);
-			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(155, 13);
-			this.label2.TabIndex = 1;
-			this.label2.Text = "Tags to Add When Skin In Use";
-			// 
 			// gridRemove
 			// 
 			this.gridRemove.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left)));
+			this.gridRemove.BackgroundColor = System.Drawing.Color.White;
+			this.gridRemove.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridRemove.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle7.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle7.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle7.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle7.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle7.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle7.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridRemove.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle7;
 			this.gridRemove.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridRemove.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColTag,
             this.ColRemove});
+			dataGridViewCellStyle8.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle8.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle8.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle8.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle8.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle8.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridRemove.DefaultCellStyle = dataGridViewCellStyle8;
 			this.gridRemove.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
-			this.gridRemove.Location = new System.Drawing.Point(7, 16);
+			this.gridRemove.EnableHeadersVisualStyles = false;
+			this.gridRemove.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridRemove.GridColor = System.Drawing.Color.LightGray;
+			this.gridRemove.Location = new System.Drawing.Point(6, 25);
 			this.gridRemove.MultiSelect = false;
 			this.gridRemove.Name = "gridRemove";
+			this.gridRemove.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle9.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle9.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle9.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle9.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle9.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle9.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridRemove.RowHeadersDefaultCellStyle = dataGridViewCellStyle9;
 			this.gridRemove.RowHeadersVisible = false;
-			this.gridRemove.Size = new System.Drawing.Size(275, 552);
+			this.gridRemove.Size = new System.Drawing.Size(316, 534);
 			this.gridRemove.TabIndex = 2;
 			// 
+			// ColTag
+			// 
+			this.ColTag.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.ColTag.HeaderText = "Tag";
+			this.ColTag.Name = "ColTag";
+			this.ColTag.ReadOnly = true;
+			// 
+			// ColRemove
+			// 
+			this.ColRemove.HeaderText = "Remove?";
+			this.ColRemove.Name = "ColRemove";
+			this.ColRemove.Width = 75;
+			// 
 			// gridAdd
 			// 
 			this.gridAdd.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left)));
+			this.gridAdd.BackgroundColor = System.Drawing.Color.White;
+			this.gridAdd.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridAdd.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle10.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle10.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle10.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle10.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle10.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle10.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle10.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridAdd.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle10;
 			this.gridAdd.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridAdd.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColTagAdd});
+			dataGridViewCellStyle11.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle11.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle11.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle11.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle11.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle11.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle11.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridAdd.DefaultCellStyle = dataGridViewCellStyle11;
 			this.gridAdd.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
-			this.gridAdd.Location = new System.Drawing.Point(371, 16);
+			this.gridAdd.EnableHeadersVisualStyles = false;
+			this.gridAdd.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridAdd.GridColor = System.Drawing.Color.LightGray;
+			this.gridAdd.Location = new System.Drawing.Point(6, 25);
 			this.gridAdd.MultiSelect = false;
 			this.gridAdd.Name = "gridAdd";
+			this.gridAdd.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle12.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle12.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle12.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle12.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle12.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle12.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle12.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridAdd.RowHeadersDefaultCellStyle = dataGridViewCellStyle12;
 			this.gridAdd.RowHeadersVisible = false;
-			this.gridAdd.Size = new System.Drawing.Size(275, 552);
+			this.gridAdd.Size = new System.Drawing.Size(316, 533);
 			this.gridAdd.TabIndex = 3;
 			this.gridAdd.EditingControlShowing += new System.Windows.Forms.DataGridViewEditingControlShowingEventHandler(this.gridAdd_EditingControlShowing);
 			// 
@@ -95,44 +162,53 @@
 			this.ColTagAdd.HeaderText = "Tag";
 			this.ColTagAdd.Name = "ColTagAdd";
 			// 
-			// ColTag
+			// skinnedGroupBox1
 			// 
-			this.ColTag.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
-			this.ColTag.HeaderText = "Tag";
-			this.ColTag.Name = "ColTag";
-			this.ColTag.ReadOnly = true;
+			this.skinnedGroupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+			this.skinnedGroupBox1.Controls.Add(this.gridRemove);
+			this.skinnedGroupBox1.Location = new System.Drawing.Point(3, 3);
+			this.skinnedGroupBox1.Name = "skinnedGroupBox1";
+			this.skinnedGroupBox1.Size = new System.Drawing.Size(328, 565);
+			this.skinnedGroupBox1.TabIndex = 4;
+			this.skinnedGroupBox1.TabStop = false;
+			this.skinnedGroupBox1.Text = "Tags to Remove When Skin in Use";
 			// 
-			// ColRemove
+			// skinnedGroupBox2
 			// 
-			this.ColRemove.HeaderText = "Remove?";
-			this.ColRemove.Name = "ColRemove";
-			this.ColRemove.Width = 75;
+			this.skinnedGroupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+			this.skinnedGroupBox2.Controls.Add(this.gridAdd);
+			this.skinnedGroupBox2.Location = new System.Drawing.Point(337, 3);
+			this.skinnedGroupBox2.Name = "skinnedGroupBox2";
+			this.skinnedGroupBox2.Size = new System.Drawing.Size(328, 565);
+			this.skinnedGroupBox2.TabIndex = 5;
+			this.skinnedGroupBox2.TabStop = false;
+			this.skinnedGroupBox2.Text = "Tags to Add When Skin in Use";
 			// 
 			// SkinTagEditor
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.gridAdd);
-			this.Controls.Add(this.gridRemove);
-			this.Controls.Add(this.label2);
-			this.Controls.Add(this.label1);
+			this.Controls.Add(this.skinnedGroupBox2);
+			this.Controls.Add(this.skinnedGroupBox1);
 			this.Name = "SkinTagEditor";
 			this.Size = new System.Drawing.Size(756, 571);
 			((System.ComponentModel.ISupportInitialize)(this.gridRemove)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridAdd)).EndInit();
+			this.skinnedGroupBox1.ResumeLayout(false);
+			this.skinnedGroupBox2.ResumeLayout(false);
 			this.ResumeLayout(false);
-			this.PerformLayout();
 
 		}
 
 		#endregion
-
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.DataGridView gridRemove;
-		private System.Windows.Forms.DataGridView gridAdd;
+		private Desktop.Skinning.SkinnedDataGridView gridRemove;
+		private Desktop.Skinning.SkinnedDataGridView gridAdd;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColTagAdd;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColTag;
-		private System.Windows.Forms.DataGridViewCheckBoxColumn ColRemove;
+		private Desktop.Skinning.SkinnedDataGridViewCheckBoxColumn ColRemove;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox1;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox2;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/SkinTagEditor.resx b/editor source/SPNATI Character Editor/Activities/SkinTagEditor.resx
index 40810dc27f302ff9a468b2fd21c15aa1e154ed78..163125bfc9c3f672fbfb9df3e1c7c9e573298ffb 100644
--- a/editor source/SPNATI Character Editor/Activities/SkinTagEditor.resx	
+++ b/editor source/SPNATI Character Editor/Activities/SkinTagEditor.resx	
@@ -126,7 +126,4 @@
   <metadata name="ColTagAdd.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
-  <metadata name="ColTagAdd.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>True</value>
-  </metadata>
 </root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Activities/SpellCheck.Designer.cs b/editor source/SPNATI Character Editor/Activities/SpellCheck.Designer.cs
index 6a20db784ee1644d3d74496991c68d418c99720e..6caee9d1af4235241ccc95ee1b2d7958221497da 100644
--- a/editor source/SPNATI Character Editor/Activities/SpellCheck.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/SpellCheck.Designer.cs	
@@ -29,19 +29,19 @@
 		private void InitializeComponent()
 		{
 			this.txtLine = new System.Windows.Forms.RichTextBox();
-			this.label1 = new System.Windows.Forms.Label();
-			this.cmdIgnore = new System.Windows.Forms.Button();
-			this.cmdIgnoreAll = new System.Windows.Forms.Button();
-			this.cmdAdd = new System.Windows.Forms.Button();
-			this.label2 = new System.Windows.Forms.Label();
-			this.txtWord = new System.Windows.Forms.TextBox();
-			this.cmdChange = new System.Windows.Forms.Button();
-			this.cmdChangeAll = new System.Windows.Forms.Button();
-			this.lstSuggestions = new System.Windows.Forms.ListBox();
-			this.label3 = new System.Windows.Forms.Label();
-			this.lblGood = new System.Windows.Forms.Label();
-			this.panelFix = new System.Windows.Forms.Panel();
-			this.lblProgress = new System.Windows.Forms.Label();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdIgnore = new Desktop.Skinning.SkinnedButton();
+			this.cmdIgnoreAll = new Desktop.Skinning.SkinnedButton();
+			this.cmdAdd = new Desktop.Skinning.SkinnedButton();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.txtWord = new Desktop.Skinning.SkinnedTextBox();
+			this.cmdChange = new Desktop.Skinning.SkinnedButton();
+			this.cmdChangeAll = new Desktop.Skinning.SkinnedButton();
+			this.lstSuggestions = new Desktop.Skinning.SkinnedListBox();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.lblGood = new Desktop.Skinning.SkinnedLabel();
+			this.panelFix = new Desktop.Skinning.SkinnedPanel();
+			this.lblProgress = new Desktop.Skinning.SkinnedLabel();
 			this.panelFix.SuspendLayout();
 			this.SuspendLayout();
 			// 
@@ -58,6 +58,8 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(3, 146);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(46, 13);
@@ -66,9 +68,12 @@
 			// 
 			// cmdIgnore
 			// 
+			this.cmdIgnore.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdIgnore.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdIgnore.Flat = false;
 			this.cmdIgnore.Location = new System.Drawing.Point(378, 3);
 			this.cmdIgnore.Name = "cmdIgnore";
-			this.cmdIgnore.Size = new System.Drawing.Size(75, 23);
+			this.cmdIgnore.Size = new System.Drawing.Size(100, 23);
 			this.cmdIgnore.TabIndex = 2;
 			this.cmdIgnore.Text = "&Ignore Once";
 			this.cmdIgnore.UseVisualStyleBackColor = true;
@@ -76,9 +81,12 @@
 			// 
 			// cmdIgnoreAll
 			// 
+			this.cmdIgnoreAll.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdIgnoreAll.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdIgnoreAll.Flat = false;
 			this.cmdIgnoreAll.Location = new System.Drawing.Point(378, 32);
 			this.cmdIgnoreAll.Name = "cmdIgnoreAll";
-			this.cmdIgnoreAll.Size = new System.Drawing.Size(75, 23);
+			this.cmdIgnoreAll.Size = new System.Drawing.Size(100, 23);
 			this.cmdIgnoreAll.TabIndex = 3;
 			this.cmdIgnoreAll.Text = "I&gnore All";
 			this.cmdIgnoreAll.UseVisualStyleBackColor = true;
@@ -86,9 +94,12 @@
 			// 
 			// cmdAdd
 			// 
+			this.cmdAdd.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdAdd.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdAdd.Flat = false;
 			this.cmdAdd.Location = new System.Drawing.Point(378, 61);
 			this.cmdAdd.Name = "cmdAdd";
-			this.cmdAdd.Size = new System.Drawing.Size(75, 23);
+			this.cmdAdd.Size = new System.Drawing.Size(100, 23);
 			this.cmdAdd.TabIndex = 4;
 			this.cmdAdd.Text = "&Add";
 			this.cmdAdd.UseVisualStyleBackColor = true;
@@ -97,6 +108,8 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label2.Location = new System.Drawing.Point(3, 0);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(88, 13);
@@ -105,6 +118,9 @@
 			// 
 			// txtWord
 			// 
+			this.txtWord.BackColor = System.Drawing.Color.White;
+			this.txtWord.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtWord.ForeColor = System.Drawing.Color.Black;
 			this.txtWord.Location = new System.Drawing.Point(6, 16);
 			this.txtWord.Name = "txtWord";
 			this.txtWord.Size = new System.Drawing.Size(366, 20);
@@ -113,9 +129,12 @@
 			// 
 			// cmdChange
 			// 
+			this.cmdChange.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdChange.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdChange.Flat = false;
 			this.cmdChange.Location = new System.Drawing.Point(378, 90);
 			this.cmdChange.Name = "cmdChange";
-			this.cmdChange.Size = new System.Drawing.Size(75, 23);
+			this.cmdChange.Size = new System.Drawing.Size(100, 23);
 			this.cmdChange.TabIndex = 5;
 			this.cmdChange.Text = "&Change";
 			this.cmdChange.UseVisualStyleBackColor = true;
@@ -123,9 +142,12 @@
 			// 
 			// cmdChangeAll
 			// 
+			this.cmdChangeAll.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdChangeAll.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdChangeAll.Flat = false;
 			this.cmdChangeAll.Location = new System.Drawing.Point(378, 119);
 			this.cmdChangeAll.Name = "cmdChangeAll";
-			this.cmdChangeAll.Size = new System.Drawing.Size(75, 23);
+			this.cmdChangeAll.Size = new System.Drawing.Size(100, 23);
 			this.cmdChangeAll.TabIndex = 6;
 			this.cmdChangeAll.Text = "Change A&ll";
 			this.cmdChangeAll.UseVisualStyleBackColor = true;
@@ -133,6 +155,9 @@
 			// 
 			// lstSuggestions
 			// 
+			this.lstSuggestions.BackColor = System.Drawing.Color.White;
+			this.lstSuggestions.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstSuggestions.ForeColor = System.Drawing.Color.Black;
 			this.lstSuggestions.FormattingEnabled = true;
 			this.lstSuggestions.Location = new System.Drawing.Point(6, 61);
 			this.lstSuggestions.Name = "lstSuggestions";
@@ -142,6 +167,8 @@
 			// label3
 			// 
 			this.label3.AutoSize = true;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label3.Location = new System.Drawing.Point(3, 42);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(68, 13);
@@ -152,14 +179,16 @@
 			// 
 			this.lblGood.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblGood.AutoSize = true;
-			this.lblGood.Font = new System.Drawing.Font("Segoe UI", 36F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.lblGood.Font = new System.Drawing.Font("Segoe UI", 28F);
 			this.lblGood.ForeColor = System.Drawing.Color.Green;
-			this.lblGood.Location = new System.Drawing.Point(47, 296);
+			this.lblGood.Highlight = Desktop.Skinning.SkinnedHighlight.Good;
+			this.lblGood.Level = Desktop.Skinning.SkinnedLabelLevel.Finished;
+			this.lblGood.Location = new System.Drawing.Point(3, 315);
 			this.lblGood.Name = "lblGood";
-			this.lblGood.Size = new System.Drawing.Size(625, 65);
+			this.lblGood.Size = new System.Drawing.Size(717, 51);
 			this.lblGood.TabIndex = 11;
 			this.lblGood.Text = "No Misspelled Words Found";
+			this.lblGood.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
 			this.lblGood.Visible = false;
 			// 
 			// panelFix
@@ -179,12 +208,15 @@
 			this.panelFix.Enabled = false;
 			this.panelFix.Location = new System.Drawing.Point(3, 3);
 			this.panelFix.Name = "panelFix";
+			this.panelFix.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.panelFix.Size = new System.Drawing.Size(603, 290);
 			this.panelFix.TabIndex = 12;
 			// 
 			// lblProgress
 			// 
 			this.lblProgress.AutoSize = true;
+			this.lblProgress.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblProgress.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.lblProgress.Location = new System.Drawing.Point(6, 231);
 			this.lblProgress.Name = "lblProgress";
 			this.lblProgress.Size = new System.Drawing.Size(34, 13);
@@ -202,25 +234,24 @@
 			this.panelFix.ResumeLayout(false);
 			this.panelFix.PerformLayout();
 			this.ResumeLayout(false);
-			this.PerformLayout();
 
 		}
 
 		#endregion
 
 		private System.Windows.Forms.RichTextBox txtLine;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Button cmdIgnore;
-		private System.Windows.Forms.Button cmdIgnoreAll;
-		private System.Windows.Forms.Button cmdAdd;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.TextBox txtWord;
-		private System.Windows.Forms.Button cmdChange;
-		private System.Windows.Forms.Button cmdChangeAll;
-		private System.Windows.Forms.ListBox lstSuggestions;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.Label lblGood;
-		private System.Windows.Forms.Panel panelFix;
-		private System.Windows.Forms.Label lblProgress;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedButton cmdIgnore;
+		private Desktop.Skinning.SkinnedButton cmdIgnoreAll;
+		private Desktop.Skinning.SkinnedButton cmdAdd;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedTextBox txtWord;
+		private Desktop.Skinning.SkinnedButton cmdChange;
+		private Desktop.Skinning.SkinnedButton cmdChangeAll;
+		private Desktop.Skinning.SkinnedListBox lstSuggestions;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedLabel lblGood;
+		private Desktop.Skinning.SkinnedPanel panelFix;
+		private Desktop.Skinning.SkinnedLabel lblProgress;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/SpellCheck.cs b/editor source/SPNATI Character Editor/Activities/SpellCheck.cs
index 8c8af88238afcfd2dd43ed136c3cfcee1ae12bb6..cead047e301e68c39b2bbbdb1fb2adbfca836cbf 100644
--- a/editor source/SPNATI Character Editor/Activities/SpellCheck.cs	
+++ b/editor source/SPNATI Character Editor/Activities/SpellCheck.cs	
@@ -1,4 +1,5 @@
 using Desktop;
+using Desktop.Skinning;
 using System;
 using System.Collections.Generic;
 using System.Drawing;
@@ -80,6 +81,12 @@ namespace SPNATI_Character_Editor.Activities
 			GetNextMisspelling();
 		}
 
+		protected override void OnSkinChanged(Skin skin)
+		{
+			txtLine.ForeColor = skin.Surface.ForeColor;
+			txtLine.BackColor = skin.FieldDisabledBackColor;
+		}
+
 		private void ProcessCase(Case workingCase)
 		{
 			foreach (DialogueLine line in workingCase.Lines)
@@ -170,6 +177,8 @@ namespace SPNATI_Character_Editor.Activities
 
 		private void DisplayWord(Misspelling misspelling)
 		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
 			_currentMisspelling = misspelling;
 			lblProgress.Text = $"Remaining: {_misspellings.Count}";
 			DisplayImage(misspelling.Case, misspelling.Line.Image);
@@ -185,7 +194,7 @@ namespace SPNATI_Character_Editor.Activities
 				txtLine.SelectionLength = word.Length;
 				txtLine.AppendText(text.Substring(0, start));
 				txtLine.SelectionFont = new Font(txtLine.Font, FontStyle.Bold);
-				txtLine.SelectionColor = Color.Red;
+				txtLine.SelectionColor = skin.BadForeColor;
 				txtLine.AppendText(word);
 				txtLine.SelectionFont = new Font(txtLine.Font, FontStyle.Regular);
 				txtLine.SelectionColor = txtLine.ForeColor;
diff --git a/editor source/SPNATI Character Editor/Activities/TagEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/TagEditor.Designer.cs
index 1a239f26bfe42b6484a84d89642cb69e428438a9..f48f4910e29a5699fa6d0e69e6c73dbd87dcc35c 100644
--- a/editor source/SPNATI Character Editor/Activities/TagEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/TagEditor.Designer.cs	
@@ -28,9 +28,9 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.toc = new System.Windows.Forms.TreeView();
-			this.mainPane = new System.Windows.Forms.Panel();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.toc = new Desktop.Skinning.SkinnedListBox();
+			this.mainPane = new Desktop.Skinning.SkinnedPanel();
 			this.tagGrid = new SPNATI_Character_Editor.Controls.TagGrid();
 			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
 			this.tagList = new SPNATI_Character_Editor.Controls.TagList();
@@ -44,6 +44,7 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(3, 0);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(264, 13);
@@ -54,15 +55,17 @@
 			// 
 			this.toc.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left)));
-			this.toc.BackColor = System.Drawing.SystemColors.Window;
+			this.toc.BackColor = System.Drawing.Color.White;
 			this.toc.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
-			this.toc.HideSelection = false;
+			this.toc.Font = new System.Drawing.Font("Arial", 9F);
+			this.toc.ForeColor = System.Drawing.Color.Black;
+			this.toc.ItemHeight = 15;
 			this.toc.Location = new System.Drawing.Point(0, 0);
 			this.toc.Margin = new System.Windows.Forms.Padding(0);
 			this.toc.Name = "toc";
-			this.toc.Size = new System.Drawing.Size(156, 569);
+			this.toc.Size = new System.Drawing.Size(156, 557);
 			this.toc.TabIndex = 110;
-			this.toc.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.toc_AfterSelect);
+			this.toc.SelectedIndexChanged += new System.EventHandler(this.toc_SelectedIndexChanged);
 			// 
 			// mainPane
 			// 
@@ -74,6 +77,7 @@
 			this.mainPane.Location = new System.Drawing.Point(159, 0);
 			this.mainPane.Margin = new System.Windows.Forms.Padding(0);
 			this.mainPane.Name = "mainPane";
+			this.mainPane.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.mainPane.Size = new System.Drawing.Size(514, 569);
 			this.mainPane.TabIndex = 111;
 			// 
@@ -137,10 +141,10 @@
 		}
 
 		#endregion
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedLabel label1;
 		private Controls.TagGrid tagGrid;
-		private System.Windows.Forms.TreeView toc;
-		private System.Windows.Forms.Panel mainPane;
+		private Desktop.Skinning.SkinnedListBox toc;
+		private Desktop.Skinning.SkinnedPanel mainPane;
 		private System.Windows.Forms.SplitContainer splitContainer1;
 		private Controls.TagList tagList;
 	}
diff --git a/editor source/SPNATI Character Editor/Activities/TagEditor.cs b/editor source/SPNATI Character Editor/Activities/TagEditor.cs
index 79b02aec5cc3a04405113f02e173e7c69ce37957..081bcec4a563a451db0ea572703408615725efa5 100644
--- a/editor source/SPNATI Character Editor/Activities/TagEditor.cs	
+++ b/editor source/SPNATI Character Editor/Activities/TagEditor.cs	
@@ -79,15 +79,14 @@ namespace SPNATI_Character_Editor.Activities
 
 				if (string.IsNullOrEmpty(group.Gender) || group.Gender == gender)
 				{
-					TreeNode node = toc.Nodes.Add(group.Label);
-					node.Tag = group;
+					toc.Items.Add(group);
 				}
 			}
 			
 			PopulateData();
-			if (toc.Nodes.Count > 0)
+			if (toc.Items.Count > 0)
 			{
-				toc.SelectedNode = toc.Nodes[0];
+				toc.SelectedIndex = 0;
 			}
 		}
 
@@ -112,10 +111,9 @@ namespace SPNATI_Character_Editor.Activities
 			_bindings.SaveIntoCharacter();
 		}
 
-		private void toc_AfterSelect(object sender, TreeViewEventArgs e)
+		private void toc_SelectedIndexChanged(object sender, System.EventArgs e)
 		{
-			TreeNode node = toc.SelectedNode;
-			TagGroup group = node.Tag as TagGroup;
+			TagGroup group = toc.SelectedItem as TagGroup;
 			tagGrid.SetGroup(group);
 			tagGrid.Visible = true;
 		}
diff --git a/editor source/SPNATI Character Editor/Activities/TemplateEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/TemplateEditor.Designer.cs
index d1cfdb5df009601935b52a4c5a2ba4553aea1529..d99bd06e2e46b9bc09a05bb9f3c312ff5be6c255 100644
--- a/editor source/SPNATI Character Editor/Activities/TemplateEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/TemplateEditor.Designer.cs	
@@ -29,31 +29,37 @@
 		private void InitializeComponent()
 		{
 			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
-			this.cmdPreviewPose = new System.Windows.Forms.Button();
-			this.label10 = new System.Windows.Forms.Label();
-			this.gridEmotions = new System.Windows.Forms.DataGridView();
-			this.label9 = new System.Windows.Forms.Label();
-			this.gridLayers = new System.Windows.Forms.DataGridView();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle5 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle6 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.cmdPreviewPose = new Desktop.Skinning.SkinnedButton();
+			this.label10 = new Desktop.Skinning.SkinnedLabel();
+			this.gridEmotions = new Desktop.Skinning.SkinnedDataGridView();
+			this.ColPoseKey = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.ColPoseL = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.ColT = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.ColR = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.ColB = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.ColCrop = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
+			this.label9 = new Desktop.Skinning.SkinnedLabel();
+			this.gridLayers = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColLayerName = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColCode = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColBlush = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColAnger = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColJuice = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.txtBaseCode = new System.Windows.Forms.TextBox();
-			this.label8 = new System.Windows.Forms.Label();
-			this.cmdGenerate = new System.Windows.Forms.Button();
-			this.cmdSaveTemplate = new System.Windows.Forms.Button();
-			this.cmdLoadTemplate = new System.Windows.Forms.Button();
+			this.txtBaseCode = new Desktop.Skinning.SkinnedTextBox();
+			this.label8 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdGenerate = new Desktop.Skinning.SkinnedButton();
+			this.cmdSaveTemplate = new Desktop.Skinning.SkinnedButton();
+			this.cmdLoadTemplate = new Desktop.Skinning.SkinnedButton();
 			this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
 			this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog();
-			this.label1 = new System.Windows.Forms.Label();
-			this.ColPoseKey = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColPoseL = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColT = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColR = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColB = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColCrop = new System.Windows.Forms.DataGridViewButtonColumn();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
 			((System.ComponentModel.ISupportInitialize)(this.gridEmotions)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridLayers)).BeginInit();
 			this.SuspendLayout();
@@ -61,9 +67,12 @@
 			// cmdPreviewPose
 			// 
 			this.cmdPreviewPose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdPreviewPose.Location = new System.Drawing.Point(814, 6);
+			this.cmdPreviewPose.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdPreviewPose.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdPreviewPose.Flat = false;
+			this.cmdPreviewPose.Location = new System.Drawing.Point(747, 6);
 			this.cmdPreviewPose.Name = "cmdPreviewPose";
-			this.cmdPreviewPose.Size = new System.Drawing.Size(114, 23);
+			this.cmdPreviewPose.Size = new System.Drawing.Size(141, 23);
 			this.cmdPreviewPose.TabIndex = 11;
 			this.cmdPreviewPose.Text = "Preview Selected";
 			this.cmdPreviewPose.UseVisualStyleBackColor = true;
@@ -72,6 +81,10 @@
 			// label10
 			// 
 			this.label10.AutoSize = true;
+			this.label10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label10.ForeColor = System.Drawing.Color.Black;
+			this.label10.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label10.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label10.Location = new System.Drawing.Point(6, 359);
 			this.label10.Name = "label10";
 			this.label10.Size = new System.Drawing.Size(39, 13);
@@ -83,6 +96,18 @@
 			this.gridEmotions.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridEmotions.BackgroundColor = System.Drawing.Color.White;
+			this.gridEmotions.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridEmotions.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridEmotions.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
 			this.gridEmotions.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridEmotions.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColPoseKey,
@@ -92,19 +117,91 @@
             this.ColB,
             this.dataGridViewTextBoxColumn1,
             this.ColCrop});
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle3.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridEmotions.DefaultCellStyle = dataGridViewCellStyle3;
 			this.gridEmotions.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
+			this.gridEmotions.EnableHeadersVisualStyles = false;
+			this.gridEmotions.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridEmotions.GridColor = System.Drawing.Color.LightGray;
 			this.gridEmotions.Location = new System.Drawing.Point(75, 359);
 			this.gridEmotions.MultiSelect = false;
 			this.gridEmotions.Name = "gridEmotions";
+			this.gridEmotions.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle4.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle4.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle4.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle4.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle4.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridEmotions.RowHeadersDefaultCellStyle = dataGridViewCellStyle4;
 			this.gridEmotions.RowHeadersVisible = false;
 			this.gridEmotions.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
 			this.gridEmotions.Size = new System.Drawing.Size(973, 207);
 			this.gridEmotions.TabIndex = 17;
 			this.gridEmotions.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridEmotions_CellContentClick);
 			// 
+			// ColPoseKey
+			// 
+			this.ColPoseKey.HeaderText = "Emotion";
+			this.ColPoseKey.Name = "ColPoseKey";
+			// 
+			// ColPoseL
+			// 
+			this.ColPoseL.HeaderText = "L";
+			this.ColPoseL.Name = "ColPoseL";
+			this.ColPoseL.Width = 40;
+			// 
+			// ColT
+			// 
+			this.ColT.HeaderText = "T";
+			this.ColT.Name = "ColT";
+			this.ColT.Width = 40;
+			// 
+			// ColR
+			// 
+			this.ColR.HeaderText = "R";
+			this.ColR.Name = "ColR";
+			this.ColR.Width = 40;
+			// 
+			// ColB
+			// 
+			this.ColB.HeaderText = "B";
+			this.ColB.Name = "ColB";
+			this.ColB.Width = 40;
+			// 
+			// dataGridViewTextBoxColumn1
+			// 
+			this.dataGridViewTextBoxColumn1.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.dataGridViewTextBoxColumn1.HeaderText = "Code";
+			this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1";
+			// 
+			// ColCrop
+			// 
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
+			dataGridViewCellStyle2.NullValue = "Crop";
+			this.ColCrop.DefaultCellStyle = dataGridViewCellStyle2;
+			this.ColCrop.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColCrop.Flat = false;
+			this.ColCrop.HeaderText = "Crop";
+			this.ColCrop.Name = "ColCrop";
+			this.ColCrop.Resizable = System.Windows.Forms.DataGridViewTriState.True;
+			this.ColCrop.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
+			this.ColCrop.Width = 60;
+			// 
 			// label9
 			// 
 			this.label9.AutoSize = true;
+			this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label9.ForeColor = System.Drawing.Color.Black;
+			this.label9.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label9.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label9.Location = new System.Drawing.Point(6, 106);
 			this.label9.Name = "label9";
 			this.label9.Size = new System.Drawing.Size(48, 13);
@@ -117,6 +214,18 @@
 			this.gridLayers.AllowUserToDeleteRows = false;
 			this.gridLayers.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridLayers.BackgroundColor = System.Drawing.Color.White;
+			this.gridLayers.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridLayers.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle5.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle5.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle5.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle5.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle5.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle5.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle5.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridLayers.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle5;
 			this.gridLayers.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridLayers.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColLayerName,
@@ -124,10 +233,30 @@
             this.ColBlush,
             this.ColAnger,
             this.ColJuice});
+			dataGridViewCellStyle6.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle6.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle6.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle6.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle6.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle6.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridLayers.DefaultCellStyle = dataGridViewCellStyle6;
 			this.gridLayers.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
+			this.gridLayers.EnableHeadersVisualStyles = false;
+			this.gridLayers.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridLayers.GridColor = System.Drawing.Color.LightGray;
 			this.gridLayers.Location = new System.Drawing.Point(75, 106);
 			this.gridLayers.MultiSelect = false;
 			this.gridLayers.Name = "gridLayers";
+			this.gridLayers.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle7.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle7.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle7.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle7.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle7.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridLayers.RowHeadersDefaultCellStyle = dataGridViewCellStyle7;
 			this.gridLayers.RowHeadersVisible = false;
 			this.gridLayers.RowHeadersWidth = 130;
 			this.gridLayers.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
@@ -170,6 +299,9 @@
 			// 
 			this.txtBaseCode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtBaseCode.BackColor = System.Drawing.Color.White;
+			this.txtBaseCode.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtBaseCode.ForeColor = System.Drawing.Color.Black;
 			this.txtBaseCode.Location = new System.Drawing.Point(75, 36);
 			this.txtBaseCode.Multiline = true;
 			this.txtBaseCode.Name = "txtBaseCode";
@@ -179,6 +311,10 @@
 			// label8
 			// 
 			this.label8.AutoSize = true;
+			this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label8.ForeColor = System.Drawing.Color.Black;
+			this.label8.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label8.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label8.Location = new System.Drawing.Point(6, 36);
 			this.label8.Name = "label8";
 			this.label8.Size = new System.Drawing.Size(61, 13);
@@ -188,9 +324,12 @@
 			// cmdGenerate
 			// 
 			this.cmdGenerate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdGenerate.Location = new System.Drawing.Point(934, 7);
+			this.cmdGenerate.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdGenerate.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdGenerate.Flat = false;
+			this.cmdGenerate.Location = new System.Drawing.Point(894, 6);
 			this.cmdGenerate.Name = "cmdGenerate";
-			this.cmdGenerate.Size = new System.Drawing.Size(114, 23);
+			this.cmdGenerate.Size = new System.Drawing.Size(154, 23);
 			this.cmdGenerate.TabIndex = 13;
 			this.cmdGenerate.Text = "Generate Pose List";
 			this.cmdGenerate.UseVisualStyleBackColor = true;
@@ -198,21 +337,27 @@
 			// 
 			// cmdSaveTemplate
 			// 
-			this.cmdSaveTemplate.Location = new System.Drawing.Point(111, 6);
+			this.cmdSaveTemplate.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdSaveTemplate.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdSaveTemplate.Flat = false;
+			this.cmdSaveTemplate.Location = new System.Drawing.Point(131, 6);
 			this.cmdSaveTemplate.Name = "cmdSaveTemplate";
-			this.cmdSaveTemplate.Size = new System.Drawing.Size(96, 23);
+			this.cmdSaveTemplate.Size = new System.Drawing.Size(118, 23);
 			this.cmdSaveTemplate.TabIndex = 10;
-			this.cmdSaveTemplate.Text = "Save Template...";
+			this.cmdSaveTemplate.Text = "Save Template";
 			this.cmdSaveTemplate.UseVisualStyleBackColor = true;
 			this.cmdSaveTemplate.Click += new System.EventHandler(this.cmdSaveTemplate_Click);
 			// 
 			// cmdLoadTemplate
 			// 
+			this.cmdLoadTemplate.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdLoadTemplate.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdLoadTemplate.Flat = false;
 			this.cmdLoadTemplate.Location = new System.Drawing.Point(7, 6);
 			this.cmdLoadTemplate.Name = "cmdLoadTemplate";
-			this.cmdLoadTemplate.Size = new System.Drawing.Size(98, 23);
+			this.cmdLoadTemplate.Size = new System.Drawing.Size(118, 23);
 			this.cmdLoadTemplate.TabIndex = 9;
-			this.cmdLoadTemplate.Text = "Load Template...";
+			this.cmdLoadTemplate.Text = "Load Template";
 			this.cmdLoadTemplate.UseVisualStyleBackColor = true;
 			this.cmdLoadTemplate.Click += new System.EventHandler(this.cmdLoadTemplate_Click);
 			// 
@@ -228,60 +373,16 @@
 			// 
 			this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
 			this.label1.AutoSize = true;
-			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.label1.ForeColor = System.Drawing.Color.Red;
-			this.label1.Location = new System.Drawing.Point(412, 572);
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Bad;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(525, 569);
 			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(636, 13);
+			this.label1.Size = new System.Drawing.Size(523, 13);
 			this.label1.TabIndex = 19;
 			this.label1.Text = "In order for templates to work properly, make sure you export your codes in Kisek" +
     "ae with appropriate filters set!";
-			// 
-			// ColPoseKey
-			// 
-			this.ColPoseKey.HeaderText = "Emotion";
-			this.ColPoseKey.Name = "ColPoseKey";
-			// 
-			// ColPoseL
-			// 
-			this.ColPoseL.HeaderText = "L";
-			this.ColPoseL.Name = "ColPoseL";
-			this.ColPoseL.Width = 40;
-			// 
-			// ColT
-			// 
-			this.ColT.HeaderText = "T";
-			this.ColT.Name = "ColT";
-			this.ColT.Width = 40;
-			// 
-			// ColR
-			// 
-			this.ColR.HeaderText = "R";
-			this.ColR.Name = "ColR";
-			this.ColR.Width = 40;
-			// 
-			// ColB
-			// 
-			this.ColB.HeaderText = "B";
-			this.ColB.Name = "ColB";
-			this.ColB.Width = 40;
-			// 
-			// dataGridViewTextBoxColumn1
-			// 
-			this.dataGridViewTextBoxColumn1.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
-			this.dataGridViewTextBoxColumn1.HeaderText = "Code";
-			this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1";
-			// 
-			// ColCrop
-			// 
-			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
-			dataGridViewCellStyle1.NullValue = "Crop";
-			this.ColCrop.DefaultCellStyle = dataGridViewCellStyle1;
-			this.ColCrop.HeaderText = "Crop";
-			this.ColCrop.Name = "ColCrop";
-			this.ColCrop.Resizable = System.Windows.Forms.DataGridViewTriState.True;
-			this.ColCrop.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
-			this.ColCrop.Width = 60;
 			// 
 			// TemplateEditor
 			// 
@@ -309,30 +410,30 @@
 
 		#endregion
 
-		private System.Windows.Forms.Button cmdPreviewPose;
-		private System.Windows.Forms.Label label10;
-		private System.Windows.Forms.DataGridView gridEmotions;
-		private System.Windows.Forms.Label label9;
-		private System.Windows.Forms.DataGridView gridLayers;
+		private Desktop.Skinning.SkinnedButton cmdPreviewPose;
+		private Desktop.Skinning.SkinnedLabel label10;
+		private Desktop.Skinning.SkinnedDataGridView gridEmotions;
+		private Desktop.Skinning.SkinnedLabel label9;
+		private Desktop.Skinning.SkinnedDataGridView gridLayers;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColLayerName;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColCode;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColBlush;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColAnger;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColJuice;
-		private System.Windows.Forms.TextBox txtBaseCode;
-		private System.Windows.Forms.Label label8;
-		private System.Windows.Forms.Button cmdGenerate;
-		private System.Windows.Forms.Button cmdSaveTemplate;
-		private System.Windows.Forms.Button cmdLoadTemplate;
+		private Desktop.Skinning.SkinnedTextBox txtBaseCode;
+		private Desktop.Skinning.SkinnedLabel label8;
+		private Desktop.Skinning.SkinnedButton cmdGenerate;
+		private Desktop.Skinning.SkinnedButton cmdSaveTemplate;
+		private Desktop.Skinning.SkinnedButton cmdLoadTemplate;
 		private System.Windows.Forms.OpenFileDialog openFileDialog1;
 		private System.Windows.Forms.SaveFileDialog saveFileDialog1;
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedLabel label1;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColPoseKey;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColPoseL;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColT;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColR;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColB;
 		private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
-		private System.Windows.Forms.DataGridViewButtonColumn ColCrop;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColCrop;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/TemplateEditor.cs b/editor source/SPNATI Character Editor/Activities/TemplateEditor.cs
index 2d6b5b42248f509e2b1f17a08d1d2097b7fb2ba2..2c9b451f0bd7963b8fad6e3e82d179489b80a977 100644
--- a/editor source/SPNATI Character Editor/Activities/TemplateEditor.cs	
+++ b/editor source/SPNATI Character Editor/Activities/TemplateEditor.cs	
@@ -51,7 +51,7 @@ namespace SPNATI_Character_Editor.Activities
 			//pull back in the old values
 			if (template != null)
 			{
-				for (int i = 0; i < gridLayers.Rows.Count - 1 && i < template.Stages.Count; i++)
+				for (int i = 0; i < gridLayers.Rows.Count && i < template.Stages.Count; i++)
 				{
 					StageTemplate stageCode = template.Stages[i];
 					DataGridViewRow row = gridLayers.Rows[i];
@@ -270,7 +270,8 @@ namespace SPNATI_Character_Editor.Activities
 				}
 				else
 				{
-					MessageBox.Show("Preview generation failed. Is Kisekae running?");
+					FailedImport import = new FailedImport();
+					import.ShowDialog();
 				}
 			}
 			Enabled = true;
diff --git a/editor source/SPNATI Character Editor/Activities/ThemeEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/ThemeEditor.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0569f5834d004447f50d02e46baf159fd193eaf3
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/ThemeEditor.Designer.cs	
@@ -0,0 +1,205 @@
+namespace SPNATI_Character_Editor.Activities
+{
+	partial class ThemeEditor
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.components = new System.ComponentModel.Container();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.skinTester1 = new Desktop.Skinning.SkinTester();
+			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+			this.gridCustom = new Desktop.Skinning.SkinnedDataGridView();
+			this.ColName = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.ColColor = new System.Windows.Forms.DataGridViewButtonColumn();
+			this.table = new Desktop.CommonControls.PropertyTable();
+			this.colorDialog1 = new System.Windows.Forms.ColorDialog();
+			this.tmrRow = new System.Windows.Forms.Timer(this.components);
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+			this.splitContainer1.Panel1.SuspendLayout();
+			this.splitContainer1.Panel2.SuspendLayout();
+			this.splitContainer1.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.gridCustom)).BeginInit();
+			this.SuspendLayout();
+			// 
+			// skinTester1
+			// 
+			this.skinTester1.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.skinTester1.Location = new System.Drawing.Point(0, 0);
+			this.skinTester1.Name = "skinTester1";
+			this.skinTester1.Size = new System.Drawing.Size(511, 686);
+			this.skinTester1.TabIndex = 0;
+			// 
+			// splitContainer1
+			// 
+			this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel2;
+			this.splitContainer1.IsSplitterFixed = true;
+			this.splitContainer1.Location = new System.Drawing.Point(0, 0);
+			this.splitContainer1.Name = "splitContainer1";
+			// 
+			// splitContainer1.Panel1
+			// 
+			this.splitContainer1.Panel1.Controls.Add(this.skinTester1);
+			// 
+			// splitContainer1.Panel2
+			// 
+			this.splitContainer1.Panel2.Controls.Add(this.gridCustom);
+			this.splitContainer1.Panel2.Controls.Add(this.table);
+			this.splitContainer1.Panel2MinSize = 450;
+			this.splitContainer1.Size = new System.Drawing.Size(975, 686);
+			this.splitContainer1.SplitterDistance = 511;
+			this.splitContainer1.TabIndex = 1;
+			// 
+			// gridCustom
+			// 
+			this.gridCustom.AllowUserToDeleteRows = false;
+			this.gridCustom.AllowUserToResizeColumns = false;
+			this.gridCustom.AllowUserToResizeRows = false;
+			this.gridCustom.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridCustom.BackgroundColor = System.Drawing.Color.White;
+			this.gridCustom.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridCustom.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridCustom.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
+			this.gridCustom.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+			this.gridCustom.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.ColName,
+            this.ColColor});
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle2.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridCustom.DefaultCellStyle = dataGridViewCellStyle2;
+			this.gridCustom.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
+			this.gridCustom.EnableHeadersVisualStyles = false;
+			this.gridCustom.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridCustom.GridColor = System.Drawing.Color.LightGray;
+			this.gridCustom.Location = new System.Drawing.Point(0, 503);
+			this.gridCustom.Margin = new System.Windows.Forms.Padding(0);
+			this.gridCustom.Name = "gridCustom";
+			this.gridCustom.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle3.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridCustom.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
+			this.gridCustom.Size = new System.Drawing.Size(460, 183);
+			this.gridCustom.TabIndex = 11;
+			this.gridCustom.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridCustom_CellContentClick);
+			// 
+			// ColName
+			// 
+			this.ColName.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.ColName.HeaderText = "Key";
+			this.ColName.Name = "ColName";
+			// 
+			// ColColor
+			// 
+			this.ColColor.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.ColColor.HeaderText = "Color";
+			this.ColColor.Name = "ColColor";
+			// 
+			// table
+			// 
+			this.table.AllowDelete = false;
+			this.table.AllowFavorites = false;
+			this.table.AllowHelp = false;
+			this.table.AllowMacros = false;
+			this.table.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.table.BackColor = System.Drawing.Color.White;
+			this.table.Data = null;
+			this.table.HeaderType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.table.HideAddField = true;
+			this.table.HideSpeedButtons = true;
+			this.table.Location = new System.Drawing.Point(0, 0);
+			this.table.ModifyingProperty = null;
+			this.table.Name = "table";
+			this.table.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.table.PlaceholderText = null;
+			this.table.PreserveControls = true;
+			this.table.PreviewData = null;
+			this.table.RemoveCaption = "Remove";
+			this.table.RowHeaderWidth = 80F;
+			this.table.RunInitialAddEvents = false;
+			this.table.Size = new System.Drawing.Size(460, 500);
+			this.table.Sorted = false;
+			this.table.TabIndex = 10;
+			this.table.UndoManager = null;
+			this.table.UseAutoComplete = false;
+			this.table.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(this.table_PropertyChanged);
+			// 
+			// tmrRow
+			// 
+			this.tmrRow.Interval = 1;
+			this.tmrRow.Tick += new System.EventHandler(this.tmrRow_Tick);
+			// 
+			// ThemeEditor
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.splitContainer1);
+			this.Name = "ThemeEditor";
+			this.Size = new System.Drawing.Size(975, 686);
+			this.splitContainer1.Panel1.ResumeLayout(false);
+			this.splitContainer1.Panel2.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+			this.splitContainer1.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.gridCustom)).EndInit();
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinTester skinTester1;
+		private System.Windows.Forms.SplitContainer splitContainer1;
+		private Desktop.CommonControls.PropertyTable table;
+		private Desktop.Skinning.SkinnedDataGridView gridCustom;
+		private System.Windows.Forms.DataGridViewTextBoxColumn ColName;
+		private System.Windows.Forms.DataGridViewButtonColumn ColColor;
+		private System.Windows.Forms.ColorDialog colorDialog1;
+		private System.Windows.Forms.Timer tmrRow;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Activities/ThemeEditor.cs b/editor source/SPNATI Character Editor/Activities/ThemeEditor.cs
new file mode 100644
index 0000000000000000000000000000000000000000..29178fc07b32823e970458423e8e3ee0e9f5847e
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/ThemeEditor.cs	
@@ -0,0 +1,122 @@
+using Desktop;
+using Desktop.Skinning;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Activities
+{
+	[Activity(typeof(Skin), 0)]
+	public partial class ThemeEditor : Activity
+	{
+		private Skin _skin;
+
+		public ThemeEditor()
+		{
+			InitializeComponent();
+		}
+
+		protected override void OnInitialize()
+		{
+			_skin = Record as Skin;
+		}
+
+		protected override void OnActivate()
+		{
+			SkinManager.Instance.SetSkin(_skin);
+		}
+
+		protected override void OnFirstActivate()
+		{
+			table.Data = _skin;
+			PopulateGrid();
+		}
+
+		public override void Save()
+		{
+			ReadGrid();
+			string json = Json.Serialize(_skin);
+			string file = Path.Combine("Resources", "Skins", _skin.Name + ".skin");
+			File.WriteAllText(file, json);
+			SkinManager.Instance.SetSkin(_skin);
+		}
+
+		private void table_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			if (e.PropertyName != "Name" && e.PropertyName != "Group")
+			{
+				SkinManager.Instance.SetSkin(table.Data as Skin);
+			}
+		}
+
+		private void PopulateGrid()
+		{
+			tmrRow.Start();
+			gridCustom.Rows.Clear();
+			foreach (KeyValuePair<string, Color> kvp in _skin.AppColors)
+			{
+				DataGridViewRow row = gridCustom.Rows[gridCustom.Rows.Add()];
+				row.Cells[ColName.Index].Value = kvp.Key;
+				DataGridViewButtonCell button = row.Cells[ColColor.Index] as DataGridViewButtonCell;
+				button.Style.BackColor = kvp.Value;
+			}
+		}
+
+		private void ReadGrid()
+		{
+			_skin.AppColors.Clear();
+			foreach (DataGridViewRow row in gridCustom.Rows)
+			{
+				string key = row.Cells[ColName.Index].Value?.ToString();
+				if (string.IsNullOrEmpty(key))
+				{
+					continue;
+				}
+				DataGridViewButtonCell button = row.Cells[ColColor.Index] as DataGridViewButtonCell;
+				Color color = button.Style.BackColor;
+				_skin.AppColors[key] = color;
+			}
+		}
+
+		private void gridCustom_CellContentClick(object sender, DataGridViewCellEventArgs e)
+		{
+			if (e.RowIndex >= 0 && e.ColumnIndex == ColColor.Index)
+			{
+				DataGridViewButtonCell button = gridCustom.Rows[e.RowIndex].Cells[ColColor.Index] as DataGridViewButtonCell;
+				colorDialog1.Color = button.Style.BackColor;
+				if (colorDialog1.ShowDialog() == DialogResult.OK)
+				{
+					button.Style.BackColor = colorDialog1.Color;
+				}
+			}
+		}
+
+		private void tmrRow_Tick(object sender, EventArgs e)
+		{
+			tmrRow.Stop();
+			RefreshColors();
+		}
+
+		private void RefreshColors()
+		{
+			foreach (DataGridViewRow row in gridCustom.Rows)
+			{
+				string key = row.Cells[ColName.Index].Value?.ToString();
+				if (string.IsNullOrEmpty(key))
+				{
+					continue;
+				}
+				DataGridViewButtonCell button = row.Cells[ColColor.Index] as DataGridViewButtonCell;
+				button.Style.BackColor = _skin.GetAppColor(key);
+			}
+		}
+
+		protected override void OnSkinChanged(Skin skin)
+		{
+			base.OnSkinChanged(skin);
+			tmrRow.Start();
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Activities/ThemeEditor.resx b/editor source/SPNATI Character Editor/Activities/ThemeEditor.resx
new file mode 100644
index 0000000000000000000000000000000000000000..c073c6ad67873a0a565766cf7efe67251963d244
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Activities/ThemeEditor.resx	
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="ColName.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="ColColor.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="colorDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <metadata name="tmrRow.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>138, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Activities/WardrobeEditor.Designer.cs b/editor source/SPNATI Character Editor/Activities/WardrobeEditor.Designer.cs
index 5e97eef849c1c4cdecbf7df443eca093c44c9e3f..2685763340e41bf8a5beb839db3091f6d27db5a9 100644
--- a/editor source/SPNATI Character Editor/Activities/WardrobeEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/WardrobeEditor.Designer.cs	
@@ -28,25 +28,28 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.gridWardrobe = new System.Windows.Forms.DataGridView();
-			this.cmdClothesDown = new System.Windows.Forms.Button();
-			this.cmdClothesUp = new System.Windows.Forms.Button();
-			this.label9 = new System.Windows.Forms.Label();
-			this.groupBox1 = new System.Windows.Forms.GroupBox();
-			this.label4 = new System.Windows.Forms.Label();
-			this.label3 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
-			this.label1 = new System.Windows.Forms.Label();
-			this.groupBox2 = new System.Windows.Forms.GroupBox();
-			this.label5 = new System.Windows.Forms.Label();
-			this.label6 = new System.Windows.Forms.Label();
-			this.label7 = new System.Windows.Forms.Label();
-			this.label8 = new System.Windows.Forms.Label();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.gridWardrobe = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColName = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColLower = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColPlural = new System.Windows.Forms.DataGridViewCheckBoxColumn();
-			this.ColType = new System.Windows.Forms.DataGridViewComboBoxColumn();
-			this.ColPosition = new System.Windows.Forms.DataGridViewComboBoxColumn();
+			this.ColPlural = new Desktop.Skinning.SkinnedDataGridViewCheckBoxColumn();
+			this.ColType = new Desktop.Skinning.SkinnedDataGridViewComboBoxColumn();
+			this.ColPosition = new Desktop.Skinning.SkinnedDataGridViewComboBoxColumn();
+			this.cmdClothesDown = new Desktop.Skinning.SkinnedButton();
+			this.cmdClothesUp = new Desktop.Skinning.SkinnedButton();
+			this.label9 = new Desktop.Skinning.SkinnedLabel();
+			this.groupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.groupBox2 = new Desktop.Skinning.SkinnedGroupBox();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
+			this.label6 = new Desktop.Skinning.SkinnedLabel();
+			this.label7 = new Desktop.Skinning.SkinnedLabel();
+			this.label8 = new Desktop.Skinning.SkinnedLabel();
 			((System.ComponentModel.ISupportInitialize)(this.gridWardrobe)).BeginInit();
 			this.groupBox1.SuspendLayout();
 			this.groupBox2.SuspendLayout();
@@ -57,6 +60,18 @@
 			this.gridWardrobe.AllowUserToResizeRows = false;
 			this.gridWardrobe.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridWardrobe.BackgroundColor = System.Drawing.Color.White;
+			this.gridWardrobe.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridWardrobe.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridWardrobe.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
 			this.gridWardrobe.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridWardrobe.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColName,
@@ -64,9 +79,29 @@
             this.ColPlural,
             this.ColType,
             this.ColPosition});
-			this.gridWardrobe.Location = new System.Drawing.Point(3, 0);
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle2.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridWardrobe.DefaultCellStyle = dataGridViewCellStyle2;
+			this.gridWardrobe.EnableHeadersVisualStyles = false;
+			this.gridWardrobe.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridWardrobe.GridColor = System.Drawing.Color.FromArgb(((int)(((byte)(142)))), ((int)(((byte)(153)))), ((int)(((byte)(243)))));
+			this.gridWardrobe.Location = new System.Drawing.Point(3, 3);
 			this.gridWardrobe.MultiSelect = false;
 			this.gridWardrobe.Name = "gridWardrobe";
+			this.gridWardrobe.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle3.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridWardrobe.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
 			this.gridWardrobe.Size = new System.Drawing.Size(939, 307);
 			this.gridWardrobe.TabIndex = 0;
 			this.gridWardrobe.CellValidated += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridWardrobe_CellValidated);
@@ -74,33 +109,81 @@
 			this.gridWardrobe.RowsAdded += new System.Windows.Forms.DataGridViewRowsAddedEventHandler(this.gridWardrobe_RowsAdded);
 			this.gridWardrobe.RowsRemoved += new System.Windows.Forms.DataGridViewRowsRemovedEventHandler(this.gridWardrobe_RowsRemoved);
 			// 
+			// ColName
+			// 
+			this.ColName.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.ColName.HeaderText = "Proper Name";
+			this.ColName.Name = "ColName";
+			this.ColName.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+			this.ColName.Visible = false;
+			// 
+			// ColLower
+			// 
+			this.ColLower.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.ColLower.HeaderText = "Name (lowercase)";
+			this.ColLower.Name = "ColLower";
+			this.ColLower.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+			// 
+			// ColPlural
+			// 
+			this.ColPlural.HeaderText = "Is Plural?";
+			this.ColPlural.Name = "ColPlural";
+			this.ColPlural.Width = 50;
+			// 
+			// ColType
+			// 
+			this.ColType.AutoComplete = false;
+			this.ColType.DisplayMember = null;
+			this.ColType.HeaderText = "Type";
+			this.ColType.Name = "ColType";
+			this.ColType.Sorted = false;
+			// 
+			// ColPosition
+			// 
+			this.ColPosition.AutoComplete = false;
+			this.ColPosition.DisplayMember = null;
+			this.ColPosition.HeaderText = "Position";
+			this.ColPosition.Name = "ColPosition";
+			this.ColPosition.Sorted = false;
+			// 
 			// cmdClothesDown
 			// 
 			this.cmdClothesDown.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdClothesDown.Location = new System.Drawing.Point(948, 39);
+			this.cmdClothesDown.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdClothesDown.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdClothesDown.Flat = true;
+			this.cmdClothesDown.ForeColor = System.Drawing.Color.Blue;
+			this.cmdClothesDown.Image = global::SPNATI_Character_Editor.Properties.Resources.DownArrow;
+			this.cmdClothesDown.Location = new System.Drawing.Point(948, 42);
 			this.cmdClothesDown.Name = "cmdClothesDown";
 			this.cmdClothesDown.Size = new System.Drawing.Size(35, 33);
 			this.cmdClothesDown.TabIndex = 6;
-			this.cmdClothesDown.Text = "â–¼";
 			this.cmdClothesDown.UseVisualStyleBackColor = true;
 			this.cmdClothesDown.Click += new System.EventHandler(this.cmdClothesDown_Click);
 			// 
 			// cmdClothesUp
 			// 
 			this.cmdClothesUp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdClothesUp.Location = new System.Drawing.Point(948, 0);
+			this.cmdClothesUp.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdClothesUp.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdClothesUp.Flat = true;
+			this.cmdClothesUp.ForeColor = System.Drawing.Color.Blue;
+			this.cmdClothesUp.Image = global::SPNATI_Character_Editor.Properties.Resources.UpArrow;
+			this.cmdClothesUp.Location = new System.Drawing.Point(948, 3);
 			this.cmdClothesUp.Name = "cmdClothesUp";
 			this.cmdClothesUp.Size = new System.Drawing.Size(35, 33);
 			this.cmdClothesUp.TabIndex = 5;
-			this.cmdClothesUp.Text = "â–²";
 			this.cmdClothesUp.UseVisualStyleBackColor = true;
 			this.cmdClothesUp.Click += new System.EventHandler(this.cmdClothesUp_Click);
 			// 
 			// label9
 			// 
-			this.label9.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
 			this.label9.AutoSize = true;
-			this.label9.Location = new System.Drawing.Point(3, 310);
+			this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label9.ForeColor = System.Drawing.Color.Black;
+			this.label9.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label9.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label9.Location = new System.Drawing.Point(3, 313);
 			this.label9.Name = "label9";
 			this.label9.Size = new System.Drawing.Size(296, 13);
 			this.label9.TabIndex = 15;
@@ -112,9 +195,9 @@
 			this.groupBox1.Controls.Add(this.label3);
 			this.groupBox1.Controls.Add(this.label2);
 			this.groupBox1.Controls.Add(this.label1);
-			this.groupBox1.Location = new System.Drawing.Point(6, 339);
+			this.groupBox1.Location = new System.Drawing.Point(6, 342);
 			this.groupBox1.Name = "groupBox1";
-			this.groupBox1.Size = new System.Drawing.Size(307, 76);
+			this.groupBox1.Size = new System.Drawing.Size(307, 99);
 			this.groupBox1.TabIndex = 16;
 			this.groupBox1.TabStop = false;
 			this.groupBox1.Text = "Type Glossary";
@@ -122,7 +205,11 @@
 			// label4
 			// 
 			this.label4.AutoSize = true;
-			this.label4.Location = new System.Drawing.Point(6, 55);
+			this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label4.ForeColor = System.Drawing.Color.Black;
+			this.label4.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label4.Location = new System.Drawing.Point(6, 64);
 			this.label4.Name = "label4";
 			this.label4.Size = new System.Drawing.Size(197, 13);
 			this.label4.TabIndex = 3;
@@ -131,7 +218,11 @@
 			// label3
 			// 
 			this.label3.AutoSize = true;
-			this.label3.Location = new System.Drawing.Point(6, 42);
+			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label3.ForeColor = System.Drawing.Color.Black;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label3.Location = new System.Drawing.Point(6, 51);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(180, 13);
 			this.label3.TabIndex = 2;
@@ -140,7 +231,11 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(6, 29);
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.Black;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label2.Location = new System.Drawing.Point(6, 38);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(182, 13);
 			this.label2.TabIndex = 1;
@@ -149,7 +244,11 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(6, 16);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.Black;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(6, 25);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(248, 13);
 			this.label1.TabIndex = 0;
@@ -161,9 +260,9 @@
 			this.groupBox2.Controls.Add(this.label6);
 			this.groupBox2.Controls.Add(this.label7);
 			this.groupBox2.Controls.Add(this.label8);
-			this.groupBox2.Location = new System.Drawing.Point(319, 339);
+			this.groupBox2.Location = new System.Drawing.Point(319, 342);
 			this.groupBox2.Name = "groupBox2";
-			this.groupBox2.Size = new System.Drawing.Size(307, 76);
+			this.groupBox2.Size = new System.Drawing.Size(307, 99);
 			this.groupBox2.TabIndex = 17;
 			this.groupBox2.TabStop = false;
 			this.groupBox2.Text = "Position Glossary";
@@ -171,7 +270,11 @@
 			// label5
 			// 
 			this.label5.AutoSize = true;
-			this.label5.Location = new System.Drawing.Point(6, 42);
+			this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label5.ForeColor = System.Drawing.Color.Black;
+			this.label5.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label5.Location = new System.Drawing.Point(6, 50);
 			this.label5.Name = "label5";
 			this.label5.Size = new System.Drawing.Size(235, 13);
 			this.label5.TabIndex = 3;
@@ -180,7 +283,11 @@
 			// label6
 			// 
 			this.label6.AutoSize = true;
-			this.label6.Location = new System.Drawing.Point(6, 55);
+			this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label6.ForeColor = System.Drawing.Color.Black;
+			this.label6.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label6.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label6.Location = new System.Drawing.Point(6, 63);
 			this.label6.Name = "label6";
 			this.label6.Size = new System.Drawing.Size(142, 13);
 			this.label6.TabIndex = 2;
@@ -189,7 +296,11 @@
 			// label7
 			// 
 			this.label7.AutoSize = true;
-			this.label7.Location = new System.Drawing.Point(6, 29);
+			this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label7.ForeColor = System.Drawing.Color.Black;
+			this.label7.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label7.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label7.Location = new System.Drawing.Point(6, 37);
 			this.label7.Name = "label7";
 			this.label7.Size = new System.Drawing.Size(149, 13);
 			this.label7.TabIndex = 1;
@@ -198,60 +309,16 @@
 			// label8
 			// 
 			this.label8.AutoSize = true;
-			this.label8.Location = new System.Drawing.Point(6, 16);
+			this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label8.ForeColor = System.Drawing.Color.Black;
+			this.label8.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label8.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label8.Location = new System.Drawing.Point(6, 24);
 			this.label8.Name = "label8";
 			this.label8.Size = new System.Drawing.Size(145, 13);
 			this.label8.TabIndex = 0;
 			this.label8.Text = "Upper: covers the chest area";
 			// 
-			// ColName
-			// 
-			this.ColName.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
-			this.ColName.HeaderText = "Proper Name";
-			this.ColName.Name = "ColName";
-			this.ColName.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-			this.ColName.Visible = false;
-			// 
-			// ColLower
-			// 
-			this.ColLower.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
-			this.ColLower.HeaderText = "Name (lowercase)";
-			this.ColLower.Name = "ColLower";
-			this.ColLower.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-			// 
-			// ColPlural
-			// 
-			this.ColPlural.HeaderText = "Is Plural?";
-			this.ColPlural.Name = "ColPlural";
-			this.ColPlural.Width = 50;
-			// 
-			// ColType
-			// 
-			this.ColType.HeaderText = "Type";
-			this.ColType.Items.AddRange(new object[] {
-            "extra",
-            "minor",
-            "major",
-            "important"});
-			this.ColType.Name = "ColType";
-			// 
-			// ColPosition
-			// 
-			this.ColPosition.HeaderText = "Position";
-			this.ColPosition.Items.AddRange(new object[] {
-            "upper",
-            "lower",
-            "both",
-            "head",
-            "neck",
-            "hands",
-            "arms",
-            "feet",
-            "legs",
-            "waist",
-            "other"});
-			this.ColPosition.Name = "ColPosition";
-			// 
 			// WardrobeEditor
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -276,24 +343,24 @@
 
 		#endregion
 
-		private System.Windows.Forms.DataGridView gridWardrobe;
-		private System.Windows.Forms.Button cmdClothesDown;
-		private System.Windows.Forms.Button cmdClothesUp;
-		private System.Windows.Forms.Label label9;
-		private System.Windows.Forms.GroupBox groupBox1;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label label4;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.GroupBox groupBox2;
-		private System.Windows.Forms.Label label6;
-		private System.Windows.Forms.Label label7;
-		private System.Windows.Forms.Label label8;
-		private System.Windows.Forms.Label label5;
+		private Desktop.Skinning.SkinnedDataGridView gridWardrobe;
+		private Desktop.Skinning.SkinnedButton cmdClothesDown;
+		private Desktop.Skinning.SkinnedButton cmdClothesUp;
+		private Desktop.Skinning.SkinnedLabel label9;
+		private Desktop.Skinning.SkinnedGroupBox groupBox1;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel label4;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedGroupBox groupBox2;
+		private Desktop.Skinning.SkinnedLabel label6;
+		private Desktop.Skinning.SkinnedLabel label7;
+		private Desktop.Skinning.SkinnedLabel label8;
+		private Desktop.Skinning.SkinnedLabel label5;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColName;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColLower;
-		private System.Windows.Forms.DataGridViewCheckBoxColumn ColPlural;
-		private System.Windows.Forms.DataGridViewComboBoxColumn ColType;
-		private System.Windows.Forms.DataGridViewComboBoxColumn ColPosition;
+		private Desktop.Skinning.SkinnedDataGridViewCheckBoxColumn ColPlural;
+		private Desktop.Skinning.SkinnedDataGridViewComboBoxColumn ColType;
+		private Desktop.Skinning.SkinnedDataGridViewComboBoxColumn ColPosition;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/WardrobeEditor.cs b/editor source/SPNATI Character Editor/Activities/WardrobeEditor.cs
index 7cbf610720371c4d6a0583228b741992724a378a..eef2a67aa9d4cfc826a8a8de92b56d385d3a5e4c 100644
--- a/editor source/SPNATI Character Editor/Activities/WardrobeEditor.cs	
+++ b/editor source/SPNATI Character Editor/Activities/WardrobeEditor.cs	
@@ -17,6 +17,8 @@ namespace SPNATI_Character_Editor.Controls
 		public WardrobeEditor()
 		{
 			InitializeComponent();
+			ColType.Items.AddRange(new object[] { "extra", "minor", "major", "important"});
+			ColPosition.Items.AddRange(new object[] { "upper", "lower", "both", "head", "neck", "hands", "arms", "feet", "legs", "waist", "other"});
 			ColPlural.TrueValue = true;
 		}
 
@@ -72,6 +74,7 @@ namespace SPNATI_Character_Editor.Controls
 		{
 			if (gridWardrobe.SelectedCells.Count > 0)
 			{
+				gridWardrobe.EndEdit();
 				SaveLayer(gridWardrobe.SelectedCells[0].OwningRow.Index);
 			}
 			ApplyWardrobeChanges();
@@ -95,10 +98,6 @@ namespace SPNATI_Character_Editor.Controls
 				layer.Type = type;
 				layer.Position = position;
 			}
-			else
-			{
-				
-			}
 		}
 
 		private void ApplyWardrobeChanges()
diff --git a/editor source/SPNATI Character Editor/Activities/WritingAid.Designer.cs b/editor source/SPNATI Character Editor/Activities/WritingAid.Designer.cs
index 6219a49c6ee69392456cef792d8e25d9adf38975..506d81e2d7a9ae9c1180b21f48a83b5e58418372 100644
--- a/editor source/SPNATI Character Editor/Activities/WritingAid.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Activities/WritingAid.Designer.cs	
@@ -28,31 +28,36 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
 			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
 			this.splitSituations = new System.Windows.Forms.SplitContainer();
-			this.chkFilter = new System.Windows.Forms.CheckBox();
-			this.label6 = new System.Windows.Forms.Label();
-			this.label5 = new System.Windows.Forms.Label();
-			this.valSuggestions = new System.Windows.Forms.NumericUpDown();
-			this.cmdRespond = new System.Windows.Forms.Button();
-			this.cmdNew = new System.Windows.Forms.Button();
-			this.label1 = new System.Windows.Forms.Label();
-			this.gridSituations = new System.Windows.Forms.DataGridView();
+			this.containerSituation = new Desktop.Skinning.SkinnedGroupBox();
+			this.lblPriority = new Desktop.Skinning.SkinnedLabel();
+			this.cboPriority = new Desktop.Skinning.SkinnedComboBox();
+			this.label6 = new Desktop.Skinning.SkinnedLabel();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdRespond = new Desktop.Skinning.SkinnedButton();
+			this.cboFilter = new Desktop.Skinning.SkinnedComboBox();
+			this.chkFilter = new Desktop.Skinning.SkinnedCheckBox();
+			this.cmdNew = new Desktop.Skinning.SkinnedButton();
+			this.valSuggestions = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.gridSituations = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColCharacter = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColName = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColDescription = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColStages = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColTrigger = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColJump = new System.Windows.Forms.DataGridViewButtonColumn();
-			this.label3 = new System.Windows.Forms.Label();
-			this.cboFilter = new System.Windows.Forms.ComboBox();
-			this.label2 = new System.Windows.Forms.Label();
+			this.ColJump = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
+			this.containerLines = new Desktop.Skinning.SkinnedGroupBox();
 			this.gridActiveSituation = new SPNATI_Character_Editor.Controls.DialogueGrid();
-			this.cmdJumpToDialogue = new System.Windows.Forms.Button();
-			this.cmdAccept = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.label4 = new System.Windows.Forms.Label();
+			this.containerResponse = new Desktop.Skinning.SkinnedGroupBox();
 			this.gridLines = new SPNATI_Character_Editor.Controls.DialogueGrid();
+			this.cmdJumpToDialogue = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.cmdAccept = new Desktop.Skinning.SkinnedButton();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
 			this.splitContainer1.Panel1.SuspendLayout();
 			this.splitContainer1.Panel2.SuspendLayout();
@@ -61,8 +66,11 @@
 			this.splitSituations.Panel1.SuspendLayout();
 			this.splitSituations.Panel2.SuspendLayout();
 			this.splitSituations.SuspendLayout();
+			this.containerSituation.SuspendLayout();
 			((System.ComponentModel.ISupportInitialize)(this.valSuggestions)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridSituations)).BeginInit();
+			this.containerLines.SuspendLayout();
+			this.containerResponse.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// splitContainer1
@@ -79,11 +87,7 @@
 			// 
 			// splitContainer1.Panel2
 			// 
-			this.splitContainer1.Panel2.Controls.Add(this.cmdJumpToDialogue);
-			this.splitContainer1.Panel2.Controls.Add(this.cmdAccept);
-			this.splitContainer1.Panel2.Controls.Add(this.cmdCancel);
-			this.splitContainer1.Panel2.Controls.Add(this.label4);
-			this.splitContainer1.Panel2.Controls.Add(this.gridLines);
+			this.splitContainer1.Panel2.Controls.Add(this.containerResponse);
 			this.splitContainer1.Size = new System.Drawing.Size(947, 642);
 			this.splitContainer1.SplitterDistance = 330;
 			this.splitContainer1.TabIndex = 0;
@@ -97,31 +101,137 @@
 			// 
 			// splitSituations.Panel1
 			// 
-			this.splitSituations.Panel1.Controls.Add(this.chkFilter);
-			this.splitSituations.Panel1.Controls.Add(this.label6);
-			this.splitSituations.Panel1.Controls.Add(this.label5);
-			this.splitSituations.Panel1.Controls.Add(this.valSuggestions);
-			this.splitSituations.Panel1.Controls.Add(this.cmdRespond);
-			this.splitSituations.Panel1.Controls.Add(this.cmdNew);
-			this.splitSituations.Panel1.Controls.Add(this.label1);
-			this.splitSituations.Panel1.Controls.Add(this.gridSituations);
-			this.splitSituations.Panel1.Controls.Add(this.label3);
-			this.splitSituations.Panel1.Controls.Add(this.cboFilter);
+			this.splitSituations.Panel1.Controls.Add(this.containerSituation);
 			// 
 			// splitSituations.Panel2
 			// 
-			this.splitSituations.Panel2.Controls.Add(this.label2);
-			this.splitSituations.Panel2.Controls.Add(this.gridActiveSituation);
+			this.splitSituations.Panel2.Controls.Add(this.containerLines);
 			this.splitSituations.Panel2Collapsed = true;
 			this.splitSituations.Size = new System.Drawing.Size(943, 326);
 			this.splitSituations.SplitterDistance = 178;
 			this.splitSituations.TabIndex = 6;
 			// 
+			// containerSituation
+			// 
+			this.containerSituation.Controls.Add(this.lblPriority);
+			this.containerSituation.Controls.Add(this.cboPriority);
+			this.containerSituation.Controls.Add(this.label6);
+			this.containerSituation.Controls.Add(this.label3);
+			this.containerSituation.Controls.Add(this.cmdRespond);
+			this.containerSituation.Controls.Add(this.cboFilter);
+			this.containerSituation.Controls.Add(this.chkFilter);
+			this.containerSituation.Controls.Add(this.cmdNew);
+			this.containerSituation.Controls.Add(this.valSuggestions);
+			this.containerSituation.Controls.Add(this.gridSituations);
+			this.containerSituation.Controls.Add(this.label5);
+			this.containerSituation.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.containerSituation.Location = new System.Drawing.Point(0, 0);
+			this.containerSituation.Name = "containerSituation";
+			this.containerSituation.Size = new System.Drawing.Size(943, 326);
+			this.containerSituation.TabIndex = 11;
+			this.containerSituation.TabStop = false;
+			this.containerSituation.Text = "Choose a Situation to Respond To";
+			// 
+			// lblPriority
+			// 
+			this.lblPriority.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.lblPriority.AutoSize = true;
+			this.lblPriority.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblPriority.ForeColor = System.Drawing.Color.Black;
+			this.lblPriority.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblPriority.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblPriority.Location = new System.Drawing.Point(235, 302);
+			this.lblPriority.Name = "lblPriority";
+			this.lblPriority.Size = new System.Drawing.Size(44, 13);
+			this.lblPriority.TabIndex = 11;
+			this.lblPriority.Text = "Filter to:";
+			// 
+			// cboPriority
+			// 
+			this.cboPriority.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.cboPriority.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboPriority.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboPriority.BackColor = System.Drawing.Color.White;
+			this.cboPriority.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboPriority.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboPriority.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboPriority.FormattingEnabled = true;
+			this.cboPriority.Location = new System.Drawing.Point(293, 299);
+			this.cboPriority.Name = "cboPriority";
+			this.cboPriority.SelectedIndex = -1;
+			this.cboPriority.SelectedItem = null;
+			this.cboPriority.Size = new System.Drawing.Size(115, 21);
+			this.cboPriority.Sorted = false;
+			this.cboPriority.TabIndex = 12;
+			this.cboPriority.SelectedIndexChanged += new System.EventHandler(this.cboFilter_SelectedIndexChanged);
+			// 
+			// label6
+			// 
+			this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.label6.AutoSize = true;
+			this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label6.ForeColor = System.Drawing.Color.Black;
+			this.label6.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label6.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label6.Location = new System.Drawing.Point(874, 6);
+			this.label6.Name = "label6";
+			this.label6.Size = new System.Drawing.Size(63, 13);
+			this.label6.TabIndex = 9;
+			this.label6.Text = "suggestions";
+			// 
+			// label3
+			// 
+			this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.label3.AutoSize = true;
+			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label3.ForeColor = System.Drawing.Color.Black;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label3.Location = new System.Drawing.Point(6, 302);
+			this.label3.Name = "label3";
+			this.label3.Size = new System.Drawing.Size(44, 13);
+			this.label3.TabIndex = 4;
+			this.label3.Text = "Filter to:";
+			// 
+			// cmdRespond
+			// 
+			this.cmdRespond.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdRespond.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdRespond.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdRespond.Flat = false;
+			this.cmdRespond.Location = new System.Drawing.Point(675, 297);
+			this.cmdRespond.Name = "cmdRespond";
+			this.cmdRespond.Size = new System.Drawing.Size(128, 23);
+			this.cmdRespond.TabIndex = 6;
+			this.cmdRespond.Text = "Respond";
+			this.cmdRespond.UseVisualStyleBackColor = true;
+			this.cmdRespond.Click += new System.EventHandler(this.cmdRespond_Click);
+			// 
+			// cboFilter
+			// 
+			this.cboFilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.cboFilter.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboFilter.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboFilter.BackColor = System.Drawing.Color.White;
+			this.cboFilter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboFilter.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboFilter.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboFilter.FormattingEnabled = true;
+			this.cboFilter.Location = new System.Drawing.Point(64, 299);
+			this.cboFilter.Name = "cboFilter";
+			this.cboFilter.SelectedIndex = -1;
+			this.cboFilter.SelectedItem = null;
+			this.cboFilter.Size = new System.Drawing.Size(165, 21);
+			this.cboFilter.Sorted = false;
+			this.cboFilter.TabIndex = 5;
+			this.cboFilter.SelectedIndexChanged += new System.EventHandler(this.cboFilter_SelectedIndexChanged);
+			// 
 			// chkFilter
 			// 
 			this.chkFilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
 			this.chkFilter.AutoSize = true;
-			this.chkFilter.Location = new System.Drawing.Point(531, 9);
+			this.chkFilter.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkFilter.Location = new System.Drawing.Point(528, 6);
 			this.chkFilter.Name = "chkFilter";
 			this.chkFilter.Size = new System.Drawing.Size(193, 17);
 			this.chkFilter.TabIndex = 10;
@@ -129,30 +239,27 @@
 			this.chkFilter.UseVisualStyleBackColor = true;
 			this.chkFilter.CheckedChanged += new System.EventHandler(this.chkFilter_CheckedChanged);
 			// 
-			// label6
-			// 
-			this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.label6.AutoSize = true;
-			this.label6.Location = new System.Drawing.Point(877, 9);
-			this.label6.Name = "label6";
-			this.label6.Size = new System.Drawing.Size(63, 13);
-			this.label6.TabIndex = 9;
-			this.label6.Text = "suggestions";
-			// 
-			// label5
+			// cmdNew
 			// 
-			this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.label5.AutoSize = true;
-			this.label5.Location = new System.Drawing.Point(769, 9);
-			this.label5.Name = "label5";
-			this.label5.Size = new System.Drawing.Size(51, 13);
-			this.label5.TabIndex = 8;
-			this.label5.Text = "Show me";
+			this.cmdNew.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdNew.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdNew.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdNew.Flat = false;
+			this.cmdNew.Location = new System.Drawing.Point(809, 297);
+			this.cmdNew.Name = "cmdNew";
+			this.cmdNew.Size = new System.Drawing.Size(128, 23);
+			this.cmdNew.TabIndex = 2;
+			this.cmdNew.Text = "New Options";
+			this.cmdNew.UseVisualStyleBackColor = true;
+			this.cmdNew.Click += new System.EventHandler(this.cmdNew_Click);
 			// 
 			// valSuggestions
 			// 
 			this.valSuggestions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.valSuggestions.Location = new System.Drawing.Point(826, 7);
+			this.valSuggestions.BackColor = System.Drawing.Color.White;
+			this.valSuggestions.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valSuggestions.ForeColor = System.Drawing.Color.Black;
+			this.valSuggestions.Location = new System.Drawing.Point(823, 4);
 			this.valSuggestions.Maximum = new decimal(new int[] {
             50,
             0,
@@ -173,37 +280,6 @@
             0});
 			this.valSuggestions.ValueChanged += new System.EventHandler(this.valSuggestions_ValueChanged);
 			// 
-			// cmdRespond
-			// 
-			this.cmdRespond.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdRespond.Location = new System.Drawing.Point(678, 300);
-			this.cmdRespond.Name = "cmdRespond";
-			this.cmdRespond.Size = new System.Drawing.Size(128, 23);
-			this.cmdRespond.TabIndex = 6;
-			this.cmdRespond.Text = "Respond";
-			this.cmdRespond.UseVisualStyleBackColor = true;
-			this.cmdRespond.Click += new System.EventHandler(this.cmdRespond_Click);
-			// 
-			// cmdNew
-			// 
-			this.cmdNew.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdNew.Location = new System.Drawing.Point(812, 300);
-			this.cmdNew.Name = "cmdNew";
-			this.cmdNew.Size = new System.Drawing.Size(128, 23);
-			this.cmdNew.TabIndex = 2;
-			this.cmdNew.Text = "Give Me New Options";
-			this.cmdNew.UseVisualStyleBackColor = true;
-			this.cmdNew.Click += new System.EventHandler(this.cmdNew_Click);
-			// 
-			// label1
-			// 
-			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(3, 14);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(162, 13);
-			this.label1.TabIndex = 1;
-			this.label1.Text = "Choose a situation to respond to:";
-			// 
 			// gridSituations
 			// 
 			this.gridSituations.AllowUserToAddRows = false;
@@ -211,6 +287,18 @@
 			this.gridSituations.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridSituations.BackgroundColor = System.Drawing.Color.White;
+			this.gridSituations.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridSituations.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridSituations.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
 			this.gridSituations.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridSituations.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColCharacter,
@@ -219,13 +307,33 @@
             this.ColStages,
             this.ColTrigger,
             this.ColJump});
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle2.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridSituations.DefaultCellStyle = dataGridViewCellStyle2;
 			this.gridSituations.EditMode = System.Windows.Forms.DataGridViewEditMode.EditProgrammatically;
-			this.gridSituations.Location = new System.Drawing.Point(6, 30);
+			this.gridSituations.EnableHeadersVisualStyles = false;
+			this.gridSituations.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridSituations.GridColor = System.Drawing.Color.LightGray;
+			this.gridSituations.Location = new System.Drawing.Point(6, 25);
 			this.gridSituations.MultiSelect = false;
 			this.gridSituations.Name = "gridSituations";
+			this.gridSituations.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle3.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridSituations.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
 			this.gridSituations.RowHeadersVisible = false;
 			this.gridSituations.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
-			this.gridSituations.Size = new System.Drawing.Size(934, 264);
+			this.gridSituations.Size = new System.Drawing.Size(931, 268);
 			this.gridSituations.TabIndex = 3;
 			this.gridSituations.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridSituations_CellContentClick);
 			this.gridSituations.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.gridSituations_CellPainting);
@@ -265,78 +373,97 @@
 			// 
 			// ColJump
 			// 
+			this.ColJump.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColJump.Flat = false;
 			this.ColJump.HeaderText = "";
 			this.ColJump.Name = "ColJump";
 			this.ColJump.Width = 21;
 			// 
-			// label3
-			// 
-			this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-			this.label3.AutoSize = true;
-			this.label3.Location = new System.Drawing.Point(3, 303);
-			this.label3.Name = "label3";
-			this.label3.Size = new System.Drawing.Size(44, 13);
-			this.label3.TabIndex = 4;
-			this.label3.Text = "Filter to:";
-			// 
-			// cboFilter
+			// label5
 			// 
-			this.cboFilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-			this.cboFilter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
-			this.cboFilter.FormattingEnabled = true;
-			this.cboFilter.Location = new System.Drawing.Point(61, 300);
-			this.cboFilter.Name = "cboFilter";
-			this.cboFilter.Size = new System.Drawing.Size(165, 21);
-			this.cboFilter.TabIndex = 5;
-			this.cboFilter.SelectedIndexChanged += new System.EventHandler(this.cboFilter_SelectedIndexChanged);
+			this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.label5.AutoSize = true;
+			this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label5.ForeColor = System.Drawing.Color.Black;
+			this.label5.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label5.Location = new System.Drawing.Point(766, 6);
+			this.label5.Name = "label5";
+			this.label5.Size = new System.Drawing.Size(51, 13);
+			this.label5.TabIndex = 8;
+			this.label5.Text = "Show me";
 			// 
-			// label2
+			// containerLines
 			// 
-			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(3, 6);
-			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(105, 13);
-			this.label2.TabIndex = 2;
-			this.label2.Text = "Lines they might say:";
+			this.containerLines.Controls.Add(this.gridActiveSituation);
+			this.containerLines.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.containerLines.Location = new System.Drawing.Point(0, 0);
+			this.containerLines.Name = "containerLines";
+			this.containerLines.Size = new System.Drawing.Size(150, 46);
+			this.containerLines.TabIndex = 4;
+			this.containerLines.TabStop = false;
+			this.containerLines.Text = "Lines They Might Say";
 			// 
 			// gridActiveSituation
 			// 
 			this.gridActiveSituation.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.gridActiveSituation.Location = new System.Drawing.Point(3, 22);
+			this.gridActiveSituation.Location = new System.Drawing.Point(6, 25);
 			this.gridActiveSituation.Name = "gridActiveSituation";
 			this.gridActiveSituation.ReadOnly = true;
-			this.gridActiveSituation.Size = new System.Drawing.Size(2523, 431);
+			this.gridActiveSituation.Size = new System.Drawing.Size(138, 15);
 			this.gridActiveSituation.TabIndex = 3;
 			this.gridActiveSituation.HighlightRow += new System.EventHandler<int>(this.gridActiveSituation_HighlightRow);
 			// 
+			// containerResponse
+			// 
+			this.containerResponse.Controls.Add(this.gridLines);
+			this.containerResponse.Controls.Add(this.cmdJumpToDialogue);
+			this.containerResponse.Controls.Add(this.cmdCancel);
+			this.containerResponse.Controls.Add(this.cmdAccept);
+			this.containerResponse.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.containerResponse.Location = new System.Drawing.Point(0, 0);
+			this.containerResponse.Name = "containerResponse";
+			this.containerResponse.Size = new System.Drawing.Size(943, 304);
+			this.containerResponse.TabIndex = 10;
+			this.containerResponse.TabStop = false;
+			this.containerResponse.Text = "Write Some Lines in Response";
+			// 
+			// gridLines
+			// 
+			this.gridLines.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridLines.Location = new System.Drawing.Point(6, 26);
+			this.gridLines.Name = "gridLines";
+			this.gridLines.ReadOnly = false;
+			this.gridLines.Size = new System.Drawing.Size(931, 245);
+			this.gridLines.TabIndex = 0;
+			this.gridLines.HighlightRow += new System.EventHandler<int>(this.gridLines_HighlightRow);
+			// 
 			// cmdJumpToDialogue
 			// 
 			this.cmdJumpToDialogue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdJumpToDialogue.Location = new System.Drawing.Point(544, 267);
+			this.cmdJumpToDialogue.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdJumpToDialogue.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdJumpToDialogue.Flat = false;
+			this.cmdJumpToDialogue.Location = new System.Drawing.Point(521, 275);
 			this.cmdJumpToDialogue.Name = "cmdJumpToDialogue";
-			this.cmdJumpToDialogue.Size = new System.Drawing.Size(128, 23);
+			this.cmdJumpToDialogue.Size = new System.Drawing.Size(148, 23);
 			this.cmdJumpToDialogue.TabIndex = 9;
-			this.cmdJumpToDialogue.Text = "Edit in Dialogue Editor";
+			this.cmdJumpToDialogue.Text = "Edit Full Screen";
 			this.cmdJumpToDialogue.UseVisualStyleBackColor = true;
 			this.cmdJumpToDialogue.Click += new System.EventHandler(this.cmdJumpToDialogue_Click);
 			// 
-			// cmdAccept
-			// 
-			this.cmdAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdAccept.Location = new System.Drawing.Point(678, 267);
-			this.cmdAccept.Name = "cmdAccept";
-			this.cmdAccept.Size = new System.Drawing.Size(128, 23);
-			this.cmdAccept.TabIndex = 8;
-			this.cmdAccept.Text = "Accept";
-			this.cmdAccept.UseVisualStyleBackColor = true;
-			this.cmdAccept.Click += new System.EventHandler(this.cmdAccept_Click);
-			// 
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdCancel.Location = new System.Drawing.Point(812, 267);
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.Blue;
+			this.cmdCancel.Location = new System.Drawing.Point(809, 275);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(128, 23);
 			this.cmdCancel.TabIndex = 7;
@@ -344,26 +471,19 @@
 			this.cmdCancel.UseVisualStyleBackColor = true;
 			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
 			// 
-			// label4
-			// 
-			this.label4.AutoSize = true;
-			this.label4.Location = new System.Drawing.Point(3, 0);
-			this.label4.Name = "label4";
-			this.label4.Size = new System.Drawing.Size(144, 13);
-			this.label4.TabIndex = 4;
-			this.label4.Text = "Write some lines in response:";
-			// 
-			// gridLines
+			// cmdAccept
 			// 
-			this.gridLines.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.gridLines.Location = new System.Drawing.Point(3, 16);
-			this.gridLines.Name = "gridLines";
-			this.gridLines.ReadOnly = false;
-			this.gridLines.Size = new System.Drawing.Size(937, 245);
-			this.gridLines.TabIndex = 0;
-			this.gridLines.HighlightRow += new System.EventHandler<int>(this.gridLines_HighlightRow);
+			this.cmdAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdAccept.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdAccept.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdAccept.Flat = false;
+			this.cmdAccept.Location = new System.Drawing.Point(675, 275);
+			this.cmdAccept.Name = "cmdAccept";
+			this.cmdAccept.Size = new System.Drawing.Size(128, 23);
+			this.cmdAccept.TabIndex = 8;
+			this.cmdAccept.Text = "Accept";
+			this.cmdAccept.UseVisualStyleBackColor = true;
+			this.cmdAccept.Click += new System.EventHandler(this.cmdAccept_Click);
 			// 
 			// WritingAid
 			// 
@@ -374,17 +494,18 @@
 			this.Size = new System.Drawing.Size(947, 642);
 			this.splitContainer1.Panel1.ResumeLayout(false);
 			this.splitContainer1.Panel2.ResumeLayout(false);
-			this.splitContainer1.Panel2.PerformLayout();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
 			this.splitContainer1.ResumeLayout(false);
 			this.splitSituations.Panel1.ResumeLayout(false);
-			this.splitSituations.Panel1.PerformLayout();
 			this.splitSituations.Panel2.ResumeLayout(false);
-			this.splitSituations.Panel2.PerformLayout();
 			((System.ComponentModel.ISupportInitialize)(this.splitSituations)).EndInit();
 			this.splitSituations.ResumeLayout(false);
+			this.containerSituation.ResumeLayout(false);
+			this.containerSituation.PerformLayout();
 			((System.ComponentModel.ISupportInitialize)(this.valSuggestions)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.gridSituations)).EndInit();
+			this.containerLines.ResumeLayout(false);
+			this.containerResponse.ResumeLayout(false);
 			this.ResumeLayout(false);
 
 		}
@@ -393,28 +514,30 @@
 
 		private System.Windows.Forms.SplitContainer splitContainer1;
 		private Controls.DialogueGrid gridLines;
-		private System.Windows.Forms.Button cmdNew;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.DataGridView gridSituations;
-		private System.Windows.Forms.ComboBox cboFilter;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.Label label4;
+		private Desktop.Skinning.SkinnedButton cmdNew;
+		private Desktop.Skinning.SkinnedDataGridView gridSituations;
+		private Desktop.Skinning.SkinnedComboBox cboFilter;
+		private Desktop.Skinning.SkinnedLabel label3;
 		private Controls.DialogueGrid gridActiveSituation;
 		private System.Windows.Forms.SplitContainer splitSituations;
-		private System.Windows.Forms.Button cmdRespond;
-		private System.Windows.Forms.Button cmdAccept;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.Label label6;
-		private System.Windows.Forms.Label label5;
-		private System.Windows.Forms.NumericUpDown valSuggestions;
-		private System.Windows.Forms.Button cmdJumpToDialogue;
-		private System.Windows.Forms.CheckBox chkFilter;
+		private Desktop.Skinning.SkinnedButton cmdRespond;
+		private Desktop.Skinning.SkinnedButton cmdAccept;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedLabel label6;
+		private Desktop.Skinning.SkinnedLabel label5;
+		private Desktop.Skinning.SkinnedNumericUpDown valSuggestions;
+		private Desktop.Skinning.SkinnedButton cmdJumpToDialogue;
+		private Desktop.Skinning.SkinnedCheckBox chkFilter;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColCharacter;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColName;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColDescription;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColStages;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColTrigger;
-		private System.Windows.Forms.DataGridViewButtonColumn ColJump;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColJump;
+		private Desktop.Skinning.SkinnedGroupBox containerSituation;
+		private Desktop.Skinning.SkinnedGroupBox containerResponse;
+		private Desktop.Skinning.SkinnedGroupBox containerLines;
+		private Desktop.Skinning.SkinnedLabel lblPriority;
+		private Desktop.Skinning.SkinnedComboBox cboPriority;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Activities/WritingAid.cs b/editor source/SPNATI Character Editor/Activities/WritingAid.cs
index 7a32f625501b3376b9d0d6c751b383cf3757bee8..96b100ac3ecbcab8c20684efe3a61accc26475c8 100644
--- a/editor source/SPNATI Character Editor/Activities/WritingAid.cs	
+++ b/editor source/SPNATI Character Editor/Activities/WritingAid.cs	
@@ -31,6 +31,9 @@ namespace SPNATI_Character_Editor.Activities
 		public WritingAid()
 		{
 			InitializeComponent();
+
+			cboPriority.Items.AddRange(new string[] { "- All -", "Must Target", "Noteworthy", "FYI" });
+			cboPriority.SelectedIndex = 0;
 		}
 
 		public override string Caption
@@ -41,6 +44,7 @@ namespace SPNATI_Character_Editor.Activities
 		protected override void OnInitialize()
 		{
 			_character = Record as Character;
+			ColJump.Flat = true;
 			_editorData = CharacterDatabase.GetEditorData(_character);
 			int suggestions = Config.GetInt(SuggestionPreference);
 			if (suggestions == 0)
@@ -70,6 +74,7 @@ namespace SPNATI_Character_Editor.Activities
 				CharacterEditorData editorData = CharacterDatabase.GetEditorData(c);
 				_maxSuggestions += editorData.NoteworthySituations.Count;
 			}
+			cboFilter.Sorted = true;
 			cboFilter.SelectedIndex = 0;
 		}
 
@@ -86,6 +91,14 @@ namespace SPNATI_Character_Editor.Activities
 		private void GenerateSuggestions()
 		{
 			Cursor.Current = Cursors.WaitCursor;
+
+			int pri = cboPriority.SelectedIndex;
+			if (pri < 0)
+			{
+				pri = 0;
+			}
+			SituationPriority priority = (SituationPriority)pri;
+
 			gridSituations.Rows.Clear();
 			int suggestionCount = (int)valSuggestions.Value;
 			int max = Math.Min(_maxSuggestions, suggestionCount);
@@ -120,7 +133,8 @@ namespace SPNATI_Character_Editor.Activities
 
 				editorData.Initialize();
 				Situation situation = editorData.NoteworthySituations.GetRandom();
-				if (suggestions.Contains(situation))
+				SituationPriority sitPriority = situation.Priority == SituationPriority.None ? SituationPriority.Noteworthy : situation.Priority;
+				if (suggestions.Contains(situation) || (priority != SituationPriority.None && priority != sitPriority))
 				{
 					continue;
 				}
diff --git a/editor source/SPNATI Character Editor/Analyzers/StatusAnalyzer.cs b/editor source/SPNATI Character Editor/Analyzers/StatusAnalyzer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4d9ee1a83dab646a301ffa4fd034c3ff44c95973
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Analyzers/StatusAnalyzer.cs	
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SPNATI_Character_Editor.Analyzers
+{
+	public class StatusAnalyzer : IDataAnalyzer
+	{
+		public string Key
+		{
+			get { return "Status"; }
+		}
+
+		public string Name
+		{
+			get { return "Listing Status"; }
+		}
+
+		public string FullName
+		{
+			get { return "Listing Status"; }
+		}
+
+		public string ParentKey
+		{
+			get { return ""; }
+		}
+
+		public string[] GetValues()
+		{
+			string[] values = new string[] {
+				"online",
+				"offline",
+				"incomplete",
+				"testing",
+				"unlisted"
+			};
+			return values;
+		}
+
+		public Type GetValueType()
+		{
+			return typeof(string);
+		}
+
+		public bool MeetsCriteria(Character character, string op, string value)
+		{
+			string status = "";
+			switch (Listing.Instance.GetCharacterStatus(character.FolderName))
+			{
+				case OpponentStatus.Incomplete:
+					status = "incomplete";
+					break;
+				case OpponentStatus.Main:
+					status = "online";
+					break;
+				case OpponentStatus.Offline:
+					status = "offline";
+					break;
+				case OpponentStatus.Testing:
+					status = "testing";
+					break;
+				case OpponentStatus.Unlisted:
+					status = "unlisted";
+					break;
+			}
+			return StringOperations.Matches(status, op, value);
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/BulkReplaceTool.Designer.cs b/editor source/SPNATI Character Editor/BulkReplaceTool.Designer.cs
index d95e6acf4170ecb748948586a2dac0a30b0c6b97..e989228a09a33aaabde3fd479a8f8f695be7e040 100644
--- a/editor source/SPNATI Character Editor/BulkReplaceTool.Designer.cs	
+++ b/editor source/SPNATI Character Editor/BulkReplaceTool.Designer.cs	
@@ -28,22 +28,28 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
-			this.gridDestinations = new System.Windows.Forms.DataGridView();
-			this.cboSource = new System.Windows.Forms.ComboBox();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle8 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle9 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.gridDestinations = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColTag = new System.Windows.Forms.DataGridViewComboBoxColumn();
-			this.label3 = new System.Windows.Forms.Label();
-			this.label4 = new System.Windows.Forms.Label();
+			this.cboSource = new Desktop.Skinning.SkinnedComboBox();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
 			((System.ComponentModel.ISupportInitialize)(this.gridDestinations)).BeginInit();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(13, 13);
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.label1.Location = new System.Drawing.Point(13, 34);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(71, 13);
 			this.label1.TabIndex = 0;
@@ -52,7 +58,8 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(272, 12);
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.label2.Location = new System.Drawing.Point(272, 33);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(94, 13);
 			this.label2.TabIndex = 1;
@@ -62,29 +69,76 @@
 			// 
 			this.gridDestinations.AllowUserToResizeColumns = false;
 			this.gridDestinations.AllowUserToResizeRows = false;
+			this.gridDestinations.BackgroundColor = System.Drawing.Color.White;
+			this.gridDestinations.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridDestinations.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle7.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle7.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle7.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle7.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle7.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle7.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridDestinations.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle7;
 			this.gridDestinations.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridDestinations.ColumnHeadersVisible = false;
 			this.gridDestinations.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColTag});
-			this.gridDestinations.Location = new System.Drawing.Point(275, 34);
+			dataGridViewCellStyle8.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle8.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle8.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle8.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle8.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle8.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridDestinations.DefaultCellStyle = dataGridViewCellStyle8;
+			this.gridDestinations.EnableHeadersVisualStyles = false;
+			this.gridDestinations.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridDestinations.GridColor = System.Drawing.Color.FromArgb(((int)(((byte)(142)))), ((int)(((byte)(153)))), ((int)(((byte)(243)))));
+			this.gridDestinations.Location = new System.Drawing.Point(275, 58);
 			this.gridDestinations.Name = "gridDestinations";
+			this.gridDestinations.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle9.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle9.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle9.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle9.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle9.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle9.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridDestinations.RowHeadersDefaultCellStyle = dataGridViewCellStyle9;
 			this.gridDestinations.RowHeadersVisible = false;
 			this.gridDestinations.Size = new System.Drawing.Size(225, 141);
 			this.gridDestinations.TabIndex = 1;
 			// 
+			// ColTag
+			// 
+			this.ColTag.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.ColTag.HeaderText = "Tag";
+			this.ColTag.Name = "ColTag";
+			// 
 			// cboSource
 			// 
+			this.cboSource.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboSource.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
 			this.cboSource.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboSource.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
 			this.cboSource.FormattingEnabled = true;
-			this.cboSource.Location = new System.Drawing.Point(16, 34);
+			this.cboSource.Location = new System.Drawing.Point(16, 58);
 			this.cboSource.Name = "cboSource";
+			this.cboSource.SelectedIndex = -1;
+			this.cboSource.SelectedItem = null;
 			this.cboSource.Size = new System.Drawing.Size(212, 21);
+			this.cboSource.Sorted = false;
 			this.cboSource.TabIndex = 0;
 			// 
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(344, 181);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.Location = new System.Drawing.Point(353, 4);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 2;
@@ -95,8 +149,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(425, 181);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(434, 4);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 3;
@@ -104,17 +162,12 @@
 			this.cmdCancel.UseVisualStyleBackColor = true;
 			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
 			// 
-			// ColTag
-			// 
-			this.ColTag.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
-			this.ColTag.HeaderText = "Tag";
-			this.ColTag.Name = "ColTag";
-			// 
 			// label3
 			// 
 			this.label3.AutoSize = true;
 			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.label3.Location = new System.Drawing.Point(239, 31);
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.label3.Location = new System.Drawing.Point(239, 54);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(30, 24);
 			this.label3.TabIndex = 4;
@@ -122,12 +175,24 @@
 			// 
 			// label4
 			// 
-			this.label4.Location = new System.Drawing.Point(13, 58);
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label4.Location = new System.Drawing.Point(13, 82);
 			this.label4.Name = "label4";
 			this.label4.Size = new System.Drawing.Size(215, 117);
 			this.label4.TabIndex = 5;
 			this.label4.Text = "This will replace all non-targeted dialogue across all stages with the dialogue f" +
     "rom the source case type.";
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 212);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(512, 30);
+			this.skinnedPanel1.TabIndex = 6;
 			// 
 			// BulkReplaceTool
 			// 
@@ -135,11 +200,10 @@
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(512, 216);
+			this.ClientSize = new System.Drawing.Size(512, 242);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.label4);
 			this.Controls.Add(this.label3);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
 			this.Controls.Add(this.cboSource);
 			this.Controls.Add(this.gridDestinations);
 			this.Controls.Add(this.label2);
@@ -147,6 +211,7 @@
 			this.Name = "BulkReplaceTool";
 			this.Text = "Bulk Replace Tool";
 			((System.ComponentModel.ISupportInitialize)(this.gridDestinations)).EndInit();
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -154,14 +219,15 @@
 
 		#endregion
 
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.DataGridView gridDestinations;
-		private System.Windows.Forms.ComboBox cboSource;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedDataGridView gridDestinations;
+		private Desktop.Skinning.SkinnedComboBox cboSource;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
 		private System.Windows.Forms.DataGridViewComboBoxColumn ColTag;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.Label label4;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedLabel label4;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/BulkReplaceTool.cs b/editor source/SPNATI Character Editor/BulkReplaceTool.cs
index 39507fff467f11f2ce477fe6d61a73e57db797b8..8ee295bef4c9e8df400353e2e5ebaab6b6a52d64 100644
--- a/editor source/SPNATI Character Editor/BulkReplaceTool.cs	
+++ b/editor source/SPNATI Character Editor/BulkReplaceTool.cs	
@@ -1,10 +1,11 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Collections.Generic;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor
 {
-	public partial class BulkReplaceTool : Form
+	public partial class BulkReplaceTool : SkinnedForm
 	{
 		private string _sourceTag;
 		public string SourceTag
@@ -49,8 +50,7 @@ namespace SPNATI_Character_Editor
 
 		private void PopulateComboBoxes()
 		{
-			cboSource.DataSource = TriggerDatabase.Triggers;
-			cboSource.BindingContext = new BindingContext();
+			cboSource.Items.AddRange(TriggerDatabase.Triggers);
 			DataGridViewComboBoxColumn column = gridDestinations.Columns[0] as DataGridViewComboBoxColumn;
 			column.Items.Add("");
 			foreach (Trigger trigger in TriggerDatabase.Triggers)
diff --git a/editor source/SPNATI Character Editor/BulkReplaceTool.resx b/editor source/SPNATI Character Editor/BulkReplaceTool.resx
index 270b0284590dbfb71bbafad9fabc98acbb8b6b30..910767c90b6e7128678e3ef611167cab21e80957 100644
--- a/editor source/SPNATI Character Editor/BulkReplaceTool.resx	
+++ b/editor source/SPNATI Character Editor/BulkReplaceTool.resx	
@@ -120,7 +120,4 @@
   <metadata name="ColTag.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
-  <metadata name="ColTag.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>True</value>
-  </metadata>
 </root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Categories/RelativePosition.cs b/editor source/SPNATI Character Editor/Categories/RelativePosition.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d3f8e2f52c759bce40f4f4787e77e2f7fbac2c69
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Categories/RelativePosition.cs	
@@ -0,0 +1,29 @@
+using Desktop;
+using Desktop.Providers;
+
+namespace SPNATI_Character_Editor
+{
+	public class RelativePosition : Category
+	{
+		public RelativePosition(string key, string value) : base(key, value)
+		{
+		}
+	}
+
+	public class PositionProvider : CategoryProvider<RelativePosition>
+	{
+		public override string GetLookupCaption()
+		{
+			return "Choose a position";
+		}
+
+		protected override RelativePosition[] GetCategoryValues()
+		{
+			return new RelativePosition[] {
+					new RelativePosition("left", "Left of me"),
+					new RelativePosition("right", "Right of me"),
+					new RelativePosition("self", "Me"),
+				};
+		}
+	}
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Categories/Slot.cs b/editor source/SPNATI Character Editor/Categories/Slot.cs
new file mode 100644
index 0000000000000000000000000000000000000000..28669b80699e1878d31e36644627954e9882dbdd
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Categories/Slot.cs	
@@ -0,0 +1,30 @@
+using Desktop;
+using Desktop.Providers;
+
+namespace SPNATI_Character_Editor
+{
+	public class Slot : Category
+	{
+		public Slot(string key, string value) : base(key, value)
+		{
+		}
+	}
+
+	public class SlotProvider : CategoryProvider<Slot>
+	{
+		public override string GetLookupCaption()
+		{
+			return "Choose a slot";
+		}
+
+		protected override Slot[] GetCategoryValues()
+		{
+			return new Slot[] {
+					new Slot("1", "Outer Left"),
+					new Slot("2", "Inner Left"),
+					new Slot("3", "Inner Right"),
+					new Slot("4", "Outer Right")
+				};
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/CharacterDatabase.cs b/editor source/SPNATI Character Editor/CharacterDatabase.cs
index a907573704ee33709b3d9af06c6abc76488ebbe8..1a803cdc0b9ff7947e10b4baaa3518772e2c15cf 100644
--- a/editor source/SPNATI Character Editor/CharacterDatabase.cs	
+++ b/editor source/SPNATI Character Editor/CharacterDatabase.cs	
@@ -94,6 +94,7 @@ namespace SPNATI_Character_Editor
 
 		public static CharacterEditorData GetEditorData(Character character)
 		{
+			if (character == null) { return new CharacterEditorData(); }
 			return _editorData.GetOrAddDefault(character, () =>
 			{
 				CharacterEditorData data = new CharacterEditorData()
@@ -115,6 +116,16 @@ namespace SPNATI_Character_Editor
 			return record.Key != "human";
 		}
 
+		/// <summary>
+		/// Record select filter for keeping out the default
+		/// </summary>
+		/// <param name="record"></param>
+		/// <returns></returns>
+		public static bool FilterDefaultCostume(IRecord record)
+		{
+			return record.Key != "default";
+		}
+
 		public static void AddSkin(Costume skin)
 		{
 			_reskins[skin.Folder] = skin;
diff --git a/editor source/SPNATI Character Editor/CharacterGenerator.cs b/editor source/SPNATI Character Editor/CharacterGenerator.cs
index 6317366dca11c23efaa83d15d32f9b296a511477..acb6a3e4a1df89b410bbc12c6fb3e3bec5e91aef 100644
--- a/editor source/SPNATI Character Editor/CharacterGenerator.cs	
+++ b/editor source/SPNATI Character Editor/CharacterGenerator.cs	
@@ -51,20 +51,31 @@ namespace SPNATI_Character_Editor
 				}
 			}
 
+			List<KisekaeCode> codes = new List<KisekaeCode>();
+			codes.Add(code);
+			ImportUnrecognizedAssets(character, codes);
+		}
+
+		public static bool ImportUnrecognizedAssets(ISkin character, List<KisekaeCode> codes)
+		{
+			bool cancelled = false;
 			List<string> unknownUrls = new List<string>();
 			string imagesDir = character.GetAttachmentsDirectory();
 
 			List<KisekaeChunk> chunks = new List<KisekaeChunk>();
-			for (int i = 0; i < code.Models.Length; i++)
+			foreach (KisekaeCode code in codes)
 			{
-				if (code.Models[i] != null)
+				for (int i = 0; i < code.Models.Length; i++)
 				{
-					chunks.Add(code.Models[i]);
+					if (code.Models[i] != null)
+					{
+						chunks.Add(code.Models[i]);
+					}
+				}
+				if (code.Scene != null)
+				{
+					chunks.Add(code.Scene);
 				}
-			}
-			if (code.Scene != null)
-			{
-				chunks.Add(code.Scene);
 			}
 
 			foreach (KisekaeChunk chunk in chunks)
@@ -89,7 +100,7 @@ namespace SPNATI_Character_Editor
 				{
 					PropImporter importer = new PropImporter();
 					importer.SetData(unknownUrls, imagesDir);
-					importer.ShowDialog();
+					cancelled = (importer.ShowDialog() == System.Windows.Forms.DialogResult.Cancel);
 				}
 				if (chunk.Assets.Count > 0)
 				{
@@ -115,6 +126,7 @@ namespace SPNATI_Character_Editor
 					chunk.ReplaceAssetPaths("images");
 				}
 			}
+			return !cancelled;
 		}
 
 		/// <summary>
diff --git a/editor source/SPNATI Character Editor/CharacterValidator.cs b/editor source/SPNATI Character Editor/CharacterValidator.cs
index 678c69e42af7089c1a3e12265b7200bbd19cd98a..d6f128aa7a85175783fe4adb68e4f64cdff036f6 100644
--- a/editor source/SPNATI Character Editor/CharacterValidator.cs	
+++ b/editor source/SPNATI Character Editor/CharacterValidator.cs	
@@ -1,9 +1,9 @@
-using System;
+using SPNATI_Character_Editor.DataStructures;
+using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Text.RegularExpressions;
 using System.Linq;
-using SPNATI_Character_Editor.Controls;
+using System.Text.RegularExpressions;
 
 namespace SPNATI_Character_Editor
 {
@@ -13,7 +13,7 @@ namespace SPNATI_Character_Editor
 		/// Validates the character's dialogue and returns a list of warnings (bad images, targets, etc.)
 		/// </summary>
 		/// <returns></returns>
-		public static bool Validate(Character character, Listing listing, out List<ValidationError> warnings)
+		public static bool Validate(Character character, out List<ValidationError> warnings)
 		{
 			warnings = new List<ValidationError>();
 			string[] hands = new string[] { "Nothing", "High Card", "One Pair", "Two Pair", "Three of a Kind", "Straight",
@@ -57,9 +57,18 @@ namespace SPNATI_Character_Editor
 			//wardrobe
 			ValidateWardrobe(character, warnings);
 
+			HashSet<string> usedCollectibles = new HashSet<string>();
+
 			//dialogue
 			foreach (Case stageCase in character.Behavior.GetWorkingCases())
 			{
+				if (stageCase.Stages.Count > 0)
+				{
+					ValidationContext context = new ValidationContext(new Stage(stageCase.Stages[0]), stageCase, null);
+					ValidateSaying(stageCase.Target, stageCase.TargetSaying, warnings, "targetSaying", stageCase.Tag, context);
+					ValidateSaying(stageCase.AlsoPlaying, stageCase.AlsoPlayingSaying, warnings, "alsoPlayingSaying", stageCase.Tag, context);
+				}
+
 				foreach (int stageIndex in stageCase.Stages)
 				{
 					Stage stage = new Stage(stageIndex);
@@ -100,21 +109,11 @@ namespace SPNATI_Character_Editor
 						{
 							if (stageCase.Target != "human")
 							{
-								warnings.Add(new ValidationError(ValidationFilterLevel.TargetedDialogue, string.Format("target \"{1}\" does not exist. {0}", caseLabel, stageCase.Target), context));
+								warnings.Add(new ValidationError(ValidationFilterLevel.MissingTargets, string.Format("target \"{1}\" does not exist. {0}", caseLabel, stageCase.Target), context));
 							}
 						}
 						else
 						{
-							OpponentStatus status = listing.GetCharacterStatus(target.FolderName);
-							if (status == OpponentStatus.Incomplete)
-							{
-								warnings.Add(new ValidationError(ValidationFilterLevel.Minor, string.Format("target \"{1}\" is flagged as incomplete. {0}", caseLabel, stageCase.Target), context));
-							}
-							else if (status == OpponentStatus.Offline)
-							{
-								warnings.Add(new ValidationError(ValidationFilterLevel.Minor, string.Format("target \"{1}\" is offline only. {0}", caseLabel, stageCase.Target), context));
-							}
-
 							if (target.FolderName != "human")
 							{
 								if (!string.IsNullOrEmpty(trigger.Gender) && target.Gender != trigger.Gender && !target.Metadata.CrossGender)
@@ -192,19 +191,7 @@ namespace SPNATI_Character_Editor
 						{
 							if (stageCase.AlsoPlaying != "human")
 							{
-								warnings.Add(new ValidationError(ValidationFilterLevel.TargetedDialogue, string.Format("alsoPlaying target \"{1}\" does not exist. {0}", caseLabel, stageCase.AlsoPlaying), context));
-							}
-						}
-						else
-						{
-							OpponentStatus status = listing.GetCharacterStatus(other.FolderName);
-							if (status == OpponentStatus.Incomplete)
-							{
-								warnings.Add(new ValidationError(ValidationFilterLevel.Minor, string.Format("alsoPlaying target \"{1}\" is flagged as incomplete. {0}", caseLabel, stageCase.AlsoPlaying), context));
-							}
-							else if (status == OpponentStatus.Offline)
-							{
-								warnings.Add(new ValidationError(ValidationFilterLevel.Minor, string.Format("alsoPlaying target \"{1}\" is offline only. {0}", caseLabel, stageCase.AlsoPlaying), context));
+								warnings.Add(new ValidationError(ValidationFilterLevel.MissingTargets, string.Format("alsoPlaying target \"{1}\" does not exist. {0}", caseLabel, stageCase.AlsoPlaying), context));
 							}
 						}
 					}
@@ -358,7 +345,7 @@ namespace SPNATI_Character_Editor
 						}
 
 						//Validate variables
-						List<string> invalidVars = DialogueLine.GetInvalidVariables(stageCase.Tag, line.Text);
+						List<string> invalidVars = DialogueLine.GetInvalidVariables(stageCase, line.Text);
 						if (invalidVars.Count > 0)
 						{
 							warnings.Add(new ValidationError(ValidationFilterLevel.Lines, string.Format("Invalid variables for case {0}: {1}", caseLabel, string.Join(",", invalidVars)), context));
@@ -385,6 +372,16 @@ namespace SPNATI_Character_Editor
 						{
 							warnings.Add(new ValidationError(ValidationFilterLevel.Lines, $"Line has mismatched <i> </i> tags: {line.Text}", context));
 						}
+
+						//validate collectibles
+						if (!string.IsNullOrEmpty(line.CollectibleId))
+						{
+							usedCollectibles.Add(line.CollectibleId);
+							if (character.Collectibles.Get(line.CollectibleId) == null)
+							{
+								warnings.Add(new ValidationError(ValidationFilterLevel.Collectibles, string.Format("Unknown collectible for case {0}: {1}", caseLabel, line.CollectibleId), context));
+							}
+						}
 					}
 				}
 			}
@@ -400,6 +397,11 @@ namespace SPNATI_Character_Editor
 				ValidatePose(character, pose, unusedImages);
 			}
 
+			foreach (Collectible collectible in character.Collectibles.Collectibles)
+			{
+				ValidateCollectible(character, collectible, warnings, unusedImages, usedCollectibles);
+			}
+
 			if (unusedImages.Count > 0)
 			{
 				warnings.Add(new ValidationError(ValidationFilterLevel.MissingImages, string.Format("The following images are never used: {0}", string.Join(", ", unusedImages))));
@@ -552,13 +554,20 @@ namespace SPNATI_Character_Editor
 
 			if (character == null)
 			{
-				warnings.Add(new ValidationError(ValidationFilterLevel.TargetedDialogue, string.Format("Missing character for {1}. {0}", caseLabel, name), context));
+				warnings.Add(new ValidationError(ValidationFilterLevel.MissingTargets, string.Format("Missing character for {1}. {0}", caseLabel, name), context));
 			}
 			else
 			{
 				string value;
 				MarkerOperator op;
 				bool perTarget;
+
+				//check for simple typos
+				if (name.Contains(" "))
+				{
+					warnings.Add(new ValidationError(ValidationFilterLevel.Lines, $"Markers cannot contain spaces: \"{name}\". {caseLabel}", context));
+				}
+
 				name = Marker.ExtractConditionPieces(name, out op, out value, out perTarget);
 				if (!character.Markers.Contains(name))
 				{
@@ -763,10 +772,49 @@ namespace SPNATI_Character_Editor
 			}
 		}
 
+		/// <summary>
+		/// Validates a collectible
+		/// </summary>
+		private static void ValidateCollectible(Character character, Collectible collectible, List<ValidationError> warnings, HashSet<string> unusedImages, HashSet<string> usedCollectibles)
+		{
+			ValidationContext context = new ValidationContext(collectible);
+			if (!usedCollectibles.Contains(collectible.Id))
+			{
+				warnings.Add(new ValidationError(ValidationFilterLevel.Collectibles, $"Collectible {collectible.Id} is never unlocked by any dialogue.", context));
+			}
+			if (!string.IsNullOrEmpty(collectible.Thumbnail))
+			{
+				string path = GetRelativeImagePath(character, collectible.Thumbnail);
+				if (!string.IsNullOrEmpty(path))
+				{
+					unusedImages.Remove(path);
+				}
+				string fullpath = Path.Combine(Config.SpnatiDirectory, collectible.Thumbnail);
+				if (!File.Exists(fullpath))
+				{
+					warnings.Add(new ValidationError(ValidationFilterLevel.Collectibles, $"The thumbnail \"{collectible.Thumbnail}\" for collectible \"{collectible.Id}\" does not exist.", context));
+				}
+			}
+			if (!string.IsNullOrEmpty(collectible.Image))
+			{
+				string path = GetRelativeImagePath(character, collectible.Image);
+				if (!string.IsNullOrEmpty(path))
+				{
+					unusedImages.Remove(path);
+				}
+				string fullpath = Path.Combine(Config.SpnatiDirectory, collectible.Image);
+				if (!File.Exists(fullpath))
+				{
+					warnings.Add(new ValidationError(ValidationFilterLevel.Collectibles, $"The image \"{collectible.Image}\" for collectible \"{collectible.Id}\" does not exist.", context));
+				}
+			}
+		}
+
 		private static string GetRelativeImagePath(Character character, string path)
 		{
 			string characterRoot = character.GetDirectory();
-			string fullPath = Path.Combine(Config.SpnatiDirectory, "opponents", path);
+			string fullPath = Path.Combine(Config.SpnatiDirectory, path.StartsWith("opponents") ? path : Path.Combine("opponents", path));
+			fullPath = fullPath.Replace("/", "\\");
 			if (fullPath.StartsWith(characterRoot))
 			{
 				string relPath = fullPath.Substring(characterRoot.Length + 1);
@@ -780,6 +828,45 @@ namespace SPNATI_Character_Editor
 			return path;
 		}
 
+		private static void ValidateSaying(string target, string text, List<ValidationError> warnings, string conditionType, string caseLabel, ValidationContext context)
+		{
+			if (string.IsNullOrEmpty(text))
+			{
+				return;
+			}
+			Character other = CharacterDatabase.Get(target);
+			if (other != null)
+			{
+				bool found = false;
+				foreach (Stage stage in other.Behavior.Stages)
+				{
+					foreach (Case stageCase in stage.Cases)
+					{
+						foreach (DialogueLine line in stageCase.Lines)
+						{
+							if (line.Text.Contains(text))
+							{
+								found = true;
+								break;
+							}
+						}
+						if (found)
+						{
+							break;
+						}
+					}
+					if (found)
+					{
+						break;
+					}
+				}
+				if (!found)
+				{
+					warnings.Add(new ValidationError(ValidationFilterLevel.TargetedDialogue, $"Using {conditionType} but {target} never says this text: \"{text}\". {caseLabel}.", context));
+				}
+			}
+		}
+
 		/// <summary>
 		/// Validates an alternative skin
 		/// </summary>
@@ -906,6 +993,7 @@ namespace SPNATI_Character_Editor
 		public Epilogue Epilogue;
 		public Scene Scene;
 		public Directive Directive;
+		public Collectible Collectible;
 
 		public ValidationContext() { }
 		public ValidationContext(Stage stage, Case stageCase, DialogueLine line)
@@ -924,11 +1012,18 @@ namespace SPNATI_Character_Editor
 			Directive = directive;
 		}
 
+		public ValidationContext(Collectible collectible)
+		{
+			Collectible = collectible;
+			ContextArea = Area.Collectible;
+		}
+
 		public enum Area
 		{
 			Dialogue,
 			Epilogue,
 			Skin,
+			Collectible,
 		}
 	}
 
@@ -936,14 +1031,16 @@ namespace SPNATI_Character_Editor
 	public enum ValidationFilterLevel
 	{
 		None = 0,
-		Minor = 1,
-		MissingImages = 2,
-		Metadata = 4,
-		Lines = 8,
-		TargetedDialogue = 16,
-		Case = 32,
-		Stage = 64,
-		Epilogue = 128,
-		Reskins = 256
+		Minor = 1 << 0,
+		MissingTargets = 1 << 1,
+		MissingImages = 1 << 2,
+		Metadata = 1 << 3,
+		Lines = 1 << 4,
+		TargetedDialogue = 1 << 5,
+		Case = 1 << 6,
+		Stage = 1 << 7,
+		Epilogue = 1 << 8,
+		Reskins = 1 << 9,
+		Collectibles = 1 << 10,
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Charts/BarChart.cs b/editor source/SPNATI Character Editor/Charts/BarChart.cs
index f9798f9c5dd01e7372bfc2b7c323bf74ba17361d..ca58df8eca419f556127c88d65cc0b5f82c4965c 100644
--- a/editor source/SPNATI Character Editor/Charts/BarChart.cs	
+++ b/editor source/SPNATI Character Editor/Charts/BarChart.cs	
@@ -9,11 +9,12 @@ namespace SPNATI_Character_Editor.Charts
 	/// <summary>
 	/// Generic bar graph
 	/// </summary>
-	public partial class BarChart : UserControl, IChartControl
+	public partial class BarChart : SkinnedChart, IChartControl
 	{
 		public BarChart()
 		{
 			InitializeComponent();
+			AddChart(chart);
 		}
 
 		public void SetTitle(string title)
diff --git a/editor source/SPNATI Character Editor/Charts/Builders/ClothingBuilder.cs b/editor source/SPNATI Character Editor/Charts/Builders/ClothingBuilder.cs
new file mode 100644
index 0000000000000000000000000000000000000000..aac0faa7d1ff32b2907612f3d4f85759597da151
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Charts/Builders/ClothingBuilder.cs	
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+
+namespace SPNATI_Character_Editor.Charts.Builders
+{
+	/// <summary>
+	/// Charts custom Pose Maker poses
+	/// </summary>
+	[Chart(ChartType.Bar, 30)]
+	public class ClothingBuilder : IChartDataBuilder
+	{
+		private List<Tuple<string, int>> _data;
+
+		public string GetLabel()
+		{
+			return "Clothing Frequency";
+		}
+
+		public void GenerateData()
+		{
+			_data = new List<Tuple<string, int>>();
+			foreach (string item in ClothingDatabase.Items.Values)
+			{
+				int count = ClothingDatabase.Items.GetCount(item);
+				_data.Add(new Tuple<string, int>(item, count));
+			}
+			_data.Sort((d1, d2) =>
+			{
+				int compare = d2.Item2.CompareTo(d1.Item2);
+				if (compare == 0)
+				{
+					compare = d1.Item1.CompareTo(d2.Item1);
+				}
+				return compare;
+			});
+		}
+
+		public List<List<ChartData>> GetSeries(string view)
+		{
+			List<List<ChartData>> data = new List<List<ChartData>>();
+			List<ChartData> series0 = new List<ChartData>();
+
+			data.Add(series0);
+			for (int i = 0; i < _data.Count; i++)
+			{
+				var item = _data[i];
+				series0.Add(new ChartData(item.Item1, item.Item2));
+			}
+
+			return data;
+		}
+
+		public string GetTitle()
+		{
+			return "Clothing Frequency";
+		}
+
+		public string[] GetViews()
+		{
+			return new string[] { "All" };
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Charts/SkinnedChart.cs b/editor source/SPNATI Character Editor/Charts/SkinnedChart.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d75f938041e2031d1b6249292a7793e895a94625
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Charts/SkinnedChart.cs	
@@ -0,0 +1,55 @@
+using Desktop.Skinning;
+using System.Collections.Generic;
+using System.Windows.Forms;
+using System.Windows.Forms.DataVisualization.Charting;
+
+namespace SPNATI_Character_Editor.Charts
+{
+	public class SkinnedChart : UserControl, ISkinControl
+	{
+		private List<Chart> _charts = new List<Chart>();
+
+		public void AddChart(Chart chart)
+		{
+			_charts.Add(chart);
+			ApplySkin(SkinManager.Instance.CurrentSkin, chart);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			foreach (Chart chart in _charts)
+			{
+				ApplySkin(skin, chart);
+			}
+		}
+
+		private void ApplySkin(Skin skin, Chart chart)
+		{
+			chart.BackColor = skin.Background.Normal;
+			chart.ForeColor = skin.Background.ForeColor;
+			foreach (ChartArea area in chart.ChartAreas)
+			{
+				area.BackColor = skin.FieldBackColor;
+				foreach (Axis axis in area.Axes)
+				{
+					axis.TitleForeColor = skin.Background.ForeColor;
+					axis.LabelStyle.ForeColor = skin.Background.ForeColor;
+				}
+			}
+			foreach (Legend legend in chart.Legends)
+			{
+				legend.BackColor = skin.Background.Normal;
+				legend.ForeColor = skin.Background.ForeColor;
+			}
+			foreach (Title title in chart.Titles)
+			{
+				title.BackColor = skin.Background.Normal;
+				title.ForeColor = skin.Background.ForeColor;
+			}
+			foreach (Series series in chart.Series)
+			{
+				series.LabelForeColor = skin.Background.ForeColor;
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Charts/StackedBarChart.cs b/editor source/SPNATI Character Editor/Charts/StackedBarChart.cs
index bf76fd110d63eececabbc7850e34930d7213fc68..37504be7edccaa334c2bce06f34aba4d7e58741f 100644
--- a/editor source/SPNATI Character Editor/Charts/StackedBarChart.cs	
+++ b/editor source/SPNATI Character Editor/Charts/StackedBarChart.cs	
@@ -1,7 +1,5 @@
 using SPNATI_Character_Editor.Charts.Builders;
-using System;
 using System.Collections.Generic;
-using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Charts
 {
@@ -9,11 +7,12 @@ namespace SPNATI_Character_Editor.Charts
 	/// <summary>
 	/// Stacked bar graph for lines spoken to a character
 	/// </summary>
-	public partial class StackedBarChart : UserControl, IChartControl
+	public partial class StackedBarChart : SkinnedChart, IChartControl
 	{
 		public StackedBarChart()
 		{
 			InitializeComponent();
+			AddChart(chart);
 		}
 
 		public void SetTitle(string title)
diff --git a/editor source/SPNATI Character Editor/Config.cs b/editor source/SPNATI Character Editor/Config.cs
index c42b04ee3ec23842ff236f9e57a5286340841c9e..655a4dd9cc666296a80ecc535b46e7f23ac84d83 100644
--- a/editor source/SPNATI Character Editor/Config.cs	
+++ b/editor source/SPNATI Character Editor/Config.cs	
@@ -12,7 +12,7 @@ namespace SPNATI_Character_Editor
 		/// List of released versions since update tracking was added, used for determining which updates a user skipped and providing info about those
 		/// </summary>
 		public static readonly string[] VersionHistory = new string[] { "v3.0", "v3.0.1", "v3.1", "v3.2", "v3.3", "v3.3.1", "v3.4", "v3.4.1", "v3.5", "v3.6",
-			"v3.7", "v3.7.1", "v3.8", "v3.8.1", "v3.8.2", "v4.0b", "v4.0.1b", "v4.0.2b", "v4.0.3b", "v4.0", "v4.1", "v4.2", "v4.2.1", "v4.3" };
+			"v3.7", "v3.7.1", "v3.8", "v3.8.1", "v3.8.2", "v4.0b", "v4.0.1b", "v4.0.2b", "v4.0.3b", "v4.0", "v4.1", "v4.2", "v4.2.1", "v4.3", "v4.4b", "v5.0b", "v5.0" };
 
 		/// <summary>
 		/// Current Version
@@ -365,6 +365,55 @@ namespace SPNATI_Character_Editor
 			set { Set("suppressdefaultlines", value); }
 		}
 
+		public static bool UseSimpleTree
+		{
+			get { return GetBoolean("simpletree"); }
+			set { Set("simpletree", value); }
+		}
+
+		public static bool DevMode
+		{
+			get { return GetBoolean(Settings.DevMode); }
+			set { Set(Settings.DevMode, value); }
+		}
+
+		public static string Skin
+		{
+			get { return GetString("skin"); }
+			set { Set("skin", value); }
+		}
+
+		public static bool ColorTargetedLines
+		{
+			get { return GetBoolean("colortargets"); }
+			set { Set("colortargets", value); }
+		}
+
+		public static bool DisableWorkflowTracer
+		{
+			get { return GetBoolean("workflowtracer"); }
+			set { Set("workflowtracer", value); }
+		}
+
+		public static HashSet<string> AutoPauseDirectives
+		{
+			get
+			{
+				HashSet<string> set = new HashSet<string>();
+				string items = GetString("autopause") ?? "";
+				foreach (string item in items.Split(','))
+				{
+					set.Add(item);
+				}
+				return set;
+			}
+			set
+			{
+				string items = string.Join(",", value);
+				Set("autopause", items);
+			}
+		}
+
 		public static void SaveMacros(string key)
 		{
 			MacroProvider provider = new MacroProvider();
@@ -416,10 +465,11 @@ namespace SPNATI_Character_Editor
 		public static readonly string DisableIntellisense = "nointellisense";
 		public static readonly string HideNoPrefix = "hidenoprefix";
 		public static readonly string PrefixFilter = "prefixfilter";
+		public static readonly string HideImages = "safemode";
 
 		#region Settings that probably only make sense for debugging
 		public static readonly string LoadOnlyLastCharacter = "loadlast";
-		public static readonly string HideImages = "safemode";
+		public static readonly string DevMode = "devmode";
 		#endregion
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/CaseControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/CaseControl.Designer.cs
index fd525e1f678361b468a0a3b02ca4b154b186729c..1f6a99e7695a6df04d75eea192c527cf8fc6c837 100644
--- a/editor source/SPNATI Character Editor/Controls/CaseControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/CaseControl.Designer.cs	
@@ -30,43 +30,59 @@
 		{
 			this.components = new System.ComponentModel.Container();
 			this.splitCase = new System.Windows.Forms.SplitContainer();
-			this.tabCase = new System.Windows.Forms.TabControl();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.tabCase = new Desktop.Skinning.SkinnedTabControl();
 			this.tabConditions = new System.Windows.Forms.TabPage();
-			this.groupBox3 = new System.Windows.Forms.GroupBox();
-			this.chkSelectAll = new System.Windows.Forms.CheckBox();
-			this.flowStageChecks = new System.Windows.Forms.FlowLayoutPanel();
-			this.grpConditions = new System.Windows.Forms.GroupBox();
-			this.valPriority = new System.Windows.Forms.NumericUpDown();
-			this.label73 = new System.Windows.Forms.Label();
+			this.stripConditions = new Desktop.Skinning.SkinnedTabStrip();
+			this.tabsConditions = new Desktop.Skinning.SkinnedTabControl();
+			this.tabPage1 = new System.Windows.Forms.TabPage();
+			this.skinnedPanel2 = new Desktop.Skinning.SkinnedPanel();
 			this.tableConditions = new Desktop.CommonControls.PropertyTable();
+			this.valPriority = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.label73 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedGroupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.txtFolder = new Desktop.Skinning.SkinnedTextBox();
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdColorCode = new System.Windows.Forms.Button();
+			this.txtLabel = new Desktop.Skinning.SkinnedTextBox();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
+			this.label34 = new Desktop.Skinning.SkinnedLabel();
+			this.cboCaseTags = new Desktop.Skinning.SkinnedComboBox();
+			this.lblHelpText = new Desktop.Skinning.SkinnedLabel();
+			this.groupBox3 = new Desktop.Skinning.SkinnedGroupBox();
+			this.gridStages = new SPNATI_Character_Editor.Controls.StageGrid();
 			this.tabTags = new System.Windows.Forms.TabPage();
 			this.lstRemoveTags = new SPNATI_Character_Editor.Controls.RecordSelectBox();
 			this.lstAddTags = new SPNATI_Character_Editor.Controls.RecordSelectBox();
-			this.label3 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
-			this.tabs = new System.Windows.Forms.TabControl();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.stripCase = new Desktop.Skinning.SkinnedTabStrip();
+			this.containerDialogue = new Desktop.Skinning.SkinnedPanel();
+			this.tabs = new Desktop.Skinning.SkinnedTabControl();
 			this.tabDialogue = new System.Windows.Forms.TabPage();
-			this.lblAvailableVars = new System.Windows.Forms.Label();
-			this.cmdCopyAll = new System.Windows.Forms.Button();
-			this.cmdPasteAll = new System.Windows.Forms.Button();
+			this.lblAvailableVars = new Desktop.Skinning.SkinnedLabel();
+			this.cmdCopyAll = new Desktop.Skinning.SkinnedButton();
+			this.cmdPasteAll = new Desktop.Skinning.SkinnedButton();
 			this.gridDialogue = new SPNATI_Character_Editor.Controls.DialogueGrid();
 			this.tabNotes = new System.Windows.Forms.TabPage();
-			this.txtNotes = new System.Windows.Forms.TextBox();
-			this.label1 = new System.Windows.Forms.Label();
-			this.lblHelpText = new System.Windows.Forms.Label();
-			this.cboCaseTags = new System.Windows.Forms.ComboBox();
-			this.label34 = new System.Windows.Forms.Label();
+			this.txtNotes = new Desktop.Skinning.SkinnedTextBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.stripTabs = new Desktop.Skinning.SkinnedTabStrip();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
 			((System.ComponentModel.ISupportInitialize)(this.splitCase)).BeginInit();
 			this.splitCase.Panel1.SuspendLayout();
 			this.splitCase.Panel2.SuspendLayout();
 			this.splitCase.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
 			this.tabCase.SuspendLayout();
 			this.tabConditions.SuspendLayout();
-			this.groupBox3.SuspendLayout();
-			this.grpConditions.SuspendLayout();
+			this.tabsConditions.SuspendLayout();
+			this.skinnedPanel2.SuspendLayout();
 			((System.ComponentModel.ISupportInitialize)(this.valPriority)).BeginInit();
+			this.skinnedGroupBox1.SuspendLayout();
+			this.groupBox3.SuspendLayout();
 			this.tabTags.SuspendLayout();
+			this.containerDialogue.SuspendLayout();
 			this.tabs.SuspendLayout();
 			this.tabDialogue.SuspendLayout();
 			this.tabNotes.SuspendLayout();
@@ -77,115 +93,128 @@
 			this.splitCase.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.splitCase.Location = new System.Drawing.Point(0, 24);
+			this.splitCase.Location = new System.Drawing.Point(0, 0);
 			this.splitCase.Name = "splitCase";
 			this.splitCase.Orientation = System.Windows.Forms.Orientation.Horizontal;
 			// 
 			// splitCase.Panel1
 			// 
-			this.splitCase.Panel1.Controls.Add(this.tabCase);
+			this.splitCase.Panel1.Controls.Add(this.skinnedPanel1);
+			this.splitCase.Panel1.Controls.Add(this.stripCase);
 			// 
 			// splitCase.Panel2
 			// 
-			this.splitCase.Panel2.Controls.Add(this.tabs);
-			this.splitCase.Size = new System.Drawing.Size(670, 656);
-			this.splitCase.SplitterDistance = 373;
+			this.splitCase.Panel2.Controls.Add(this.containerDialogue);
+			this.splitCase.Panel2.Controls.Add(this.stripTabs);
+			this.splitCase.Size = new System.Drawing.Size(670, 680);
+			this.splitCase.SplitterDistance = 386;
 			this.splitCase.TabIndex = 62;
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedPanel1.Controls.Add(this.tabCase);
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 28);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.skinnedPanel1.Size = new System.Drawing.Size(670, 358);
+			this.skinnedPanel1.TabIndex = 98;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.Top;
+			// 
 			// tabCase
 			// 
+			this.tabCase.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
 			this.tabCase.Controls.Add(this.tabConditions);
 			this.tabCase.Controls.Add(this.tabTags);
-			this.tabCase.Dock = System.Windows.Forms.DockStyle.Fill;
-			this.tabCase.Location = new System.Drawing.Point(0, 0);
+			this.tabCase.Location = new System.Drawing.Point(1, 0);
 			this.tabCase.Name = "tabCase";
 			this.tabCase.SelectedIndex = 0;
-			this.tabCase.Size = new System.Drawing.Size(670, 373);
+			this.tabCase.Size = new System.Drawing.Size(665, 356);
 			this.tabCase.TabIndex = 96;
 			// 
 			// tabConditions
 			// 
+			this.tabConditions.BackColor = System.Drawing.Color.White;
+			this.tabConditions.Controls.Add(this.stripConditions);
+			this.tabConditions.Controls.Add(this.skinnedPanel2);
+			this.tabConditions.Controls.Add(this.valPriority);
+			this.tabConditions.Controls.Add(this.label73);
+			this.tabConditions.Controls.Add(this.skinnedGroupBox1);
 			this.tabConditions.Controls.Add(this.groupBox3);
-			this.tabConditions.Controls.Add(this.grpConditions);
+			this.tabConditions.ForeColor = System.Drawing.Color.Black;
 			this.tabConditions.Location = new System.Drawing.Point(4, 22);
 			this.tabConditions.Name = "tabConditions";
 			this.tabConditions.Padding = new System.Windows.Forms.Padding(3);
-			this.tabConditions.Size = new System.Drawing.Size(662, 347);
+			this.tabConditions.Size = new System.Drawing.Size(657, 330);
 			this.tabConditions.TabIndex = 0;
-			this.tabConditions.Text = "Conditions";
+			this.tabConditions.Text = "General";
 			// 
-			// groupBox3
+			// stripConditions
 			// 
-			this.groupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+			this.stripConditions.AddCaption = "OR";
+			this.stripConditions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.groupBox3.Controls.Add(this.chkSelectAll);
-			this.groupBox3.Controls.Add(this.flowStageChecks);
-			this.groupBox3.Location = new System.Drawing.Point(6, 6);
-			this.groupBox3.Name = "groupBox3";
-			this.groupBox3.Size = new System.Drawing.Size(650, 119);
-			this.groupBox3.TabIndex = 37;
-			this.groupBox3.TabStop = false;
-			this.groupBox3.Text = "Applies to Stages";
-			// 
-			// chkSelectAll
-			// 
-			this.chkSelectAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.chkSelectAll.AutoSize = true;
-			this.chkSelectAll.BackColor = System.Drawing.Color.White;
-			this.chkSelectAll.Location = new System.Drawing.Point(577, -1);
-			this.chkSelectAll.Name = "chkSelectAll";
-			this.chkSelectAll.Size = new System.Drawing.Size(70, 17);
-			this.chkSelectAll.TabIndex = 36;
-			this.chkSelectAll.Text = "Select All";
-			this.chkSelectAll.UseVisualStyleBackColor = false;
-			this.chkSelectAll.CheckedChanged += new System.EventHandler(this.chkSelectAll_CheckedChanged);
-			// 
-			// flowStageChecks
-			// 
-			this.flowStageChecks.AutoScroll = true;
-			this.flowStageChecks.Dock = System.Windows.Forms.DockStyle.Fill;
-			this.flowStageChecks.Location = new System.Drawing.Point(3, 16);
-			this.flowStageChecks.Name = "flowStageChecks";
-			this.flowStageChecks.Size = new System.Drawing.Size(644, 100);
-			this.flowStageChecks.TabIndex = 0;
-			// 
-			// grpConditions
-			// 
-			this.grpConditions.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+			this.stripConditions.Location = new System.Drawing.Point(6, 122);
+			this.stripConditions.Margin = new System.Windows.Forms.Padding(0);
+			this.stripConditions.Name = "stripConditions";
+			this.stripConditions.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.stripConditions.ShowAddButton = true;
+			this.stripConditions.ShowCloseButton = false;
+			this.stripConditions.Size = new System.Drawing.Size(541, 28);
+			this.stripConditions.StartMargin = 5;
+			this.stripConditions.TabControl = this.tabsConditions;
+			this.stripConditions.TabIndex = 61;
+			this.stripConditions.TabMargin = 5;
+			this.stripConditions.TabPadding = 10;
+			this.stripConditions.TabSize = -1;
+			this.stripConditions.TabType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.stripConditions.Text = "skinnedTabStrip1";
+			this.stripConditions.Vertical = false;
+			this.stripConditions.CloseButtonClicked += new System.EventHandler(this.stripConditions_CloseButtonClicked);
+			this.stripConditions.AddButtonClicked += new System.EventHandler(this.stripConditions_AddButtonClicked);
+			// 
+			// tabsConditions
+			// 
+			this.tabsConditions.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.grpConditions.Controls.Add(this.valPriority);
-			this.grpConditions.Controls.Add(this.label73);
-			this.grpConditions.Controls.Add(this.tableConditions);
-			this.grpConditions.Location = new System.Drawing.Point(6, 128);
-			this.grpConditions.Name = "grpConditions";
-			this.grpConditions.Size = new System.Drawing.Size(647, 213);
-			this.grpConditions.TabIndex = 38;
-			this.grpConditions.TabStop = false;
-			this.grpConditions.Text = "Conditions";
-			// 
-			// valPriority
-			// 
-			this.valPriority.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.valPriority.Location = new System.Drawing.Point(583, 20);
-			this.valPriority.Maximum = new decimal(new int[] {
-            10000,
-            0,
-            0,
-            0});
-			this.valPriority.Name = "valPriority";
-			this.valPriority.Size = new System.Drawing.Size(54, 20);
-			this.valPriority.TabIndex = 7;
-			// 
-			// label73
-			// 
-			this.label73.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.label73.AutoSize = true;
-			this.label73.Location = new System.Drawing.Point(538, 23);
-			this.label73.Name = "label73";
-			this.label73.Size = new System.Drawing.Size(41, 13);
-			this.label73.TabIndex = 60;
-			this.label73.Text = "Priority:";
+			this.tabsConditions.Controls.Add(this.tabPage1);
+			this.tabsConditions.Location = new System.Drawing.Point(32767, 15);
+			this.tabsConditions.Margin = new System.Windows.Forms.Padding(1);
+			this.tabsConditions.Name = "tabsConditions";
+			this.tabsConditions.SelectedIndex = 0;
+			this.tabsConditions.Size = new System.Drawing.Size(50, 50);
+			this.tabsConditions.TabIndex = 62;
+			// 
+			// tabPage1
+			// 
+			this.tabPage1.BackColor = System.Drawing.Color.White;
+			this.tabPage1.ForeColor = System.Drawing.Color.Black;
+			this.tabPage1.Location = new System.Drawing.Point(4, 22);
+			this.tabPage1.Name = "tabPage1";
+			this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
+			this.tabPage1.Size = new System.Drawing.Size(42, 24);
+			this.tabPage1.TabIndex = 0;
+			this.tabPage1.Text = "Conditions";
+			// 
+			// skinnedPanel2
+			// 
+			this.skinnedPanel2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedPanel2.Controls.Add(this.tabsConditions);
+			this.skinnedPanel2.Controls.Add(this.tableConditions);
+			this.skinnedPanel2.Location = new System.Drawing.Point(5, 149);
+			this.skinnedPanel2.Margin = new System.Windows.Forms.Padding(0);
+			this.skinnedPanel2.Name = "skinnedPanel2";
+			this.skinnedPanel2.PanelType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedPanel2.Size = new System.Drawing.Size(649, 178);
+			this.skinnedPanel2.TabIndex = 32;
+			this.skinnedPanel2.TabSide = Desktop.Skinning.TabSide.Top;
 			// 
 			// tableConditions
 			// 
@@ -196,18 +225,23 @@
 			this.tableConditions.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.tableConditions.BackColor = System.Drawing.Color.White;
 			this.tableConditions.Data = null;
+			this.tableConditions.HeaderType = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.tableConditions.HideAddField = false;
 			this.tableConditions.HideSpeedButtons = false;
-			this.tableConditions.Location = new System.Drawing.Point(6, 19);
+			this.tableConditions.Location = new System.Drawing.Point(1, 2);
+			this.tableConditions.Margin = new System.Windows.Forms.Padding(1);
+			this.tableConditions.ModifyingProperty = null;
 			this.tableConditions.Name = "tableConditions";
+			this.tableConditions.PanelType = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.tableConditions.PlaceholderText = "Add a condition";
 			this.tableConditions.PreserveControls = false;
 			this.tableConditions.PreviewData = null;
 			this.tableConditions.RemoveCaption = "Remove condition";
 			this.tableConditions.RowHeaderWidth = 0F;
 			this.tableConditions.RunInitialAddEvents = true;
-			this.tableConditions.Size = new System.Drawing.Size(635, 188);
+			this.tableConditions.Size = new System.Drawing.Size(647, 175);
 			this.tableConditions.Sorted = false;
 			this.tableConditions.TabIndex = 31;
 			this.tableConditions.UndoManager = null;
@@ -215,16 +249,197 @@
 			this.tableConditions.EditingMacro += new System.EventHandler<Desktop.CommonControls.MacroArgs>(this.tableConditions_EditingMacro);
 			this.tableConditions.MacroChanged += new System.EventHandler<Desktop.CommonControls.MacroArgs>(this.tableConditions_MacroChanged);
 			// 
+			// valPriority
+			// 
+			this.valPriority.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.valPriority.BackColor = System.Drawing.Color.White;
+			this.valPriority.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valPriority.ForeColor = System.Drawing.Color.Black;
+			this.valPriority.Location = new System.Drawing.Point(599, 127);
+			this.valPriority.Maximum = new decimal(new int[] {
+            10000,
+            0,
+            0,
+            0});
+			this.valPriority.Name = "valPriority";
+			this.valPriority.Size = new System.Drawing.Size(54, 20);
+			this.valPriority.TabIndex = 7;
+			// 
+			// label73
+			// 
+			this.label73.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.label73.AutoSize = true;
+			this.label73.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label73.ForeColor = System.Drawing.Color.Black;
+			this.label73.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label73.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label73.Location = new System.Drawing.Point(552, 129);
+			this.label73.Name = "label73";
+			this.label73.Size = new System.Drawing.Size(41, 13);
+			this.label73.TabIndex = 60;
+			this.label73.Text = "Priority:";
+			// 
+			// skinnedGroupBox1
+			// 
+			this.skinnedGroupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedGroupBox1.Controls.Add(this.txtFolder);
+			this.skinnedGroupBox1.Controls.Add(this.skinnedLabel1);
+			this.skinnedGroupBox1.Controls.Add(this.cmdColorCode);
+			this.skinnedGroupBox1.Controls.Add(this.txtLabel);
+			this.skinnedGroupBox1.Controls.Add(this.label4);
+			this.skinnedGroupBox1.Controls.Add(this.label34);
+			this.skinnedGroupBox1.Controls.Add(this.cboCaseTags);
+			this.skinnedGroupBox1.Controls.Add(this.lblHelpText);
+			this.skinnedGroupBox1.Location = new System.Drawing.Point(478, 6);
+			this.skinnedGroupBox1.Name = "skinnedGroupBox1";
+			this.skinnedGroupBox1.Size = new System.Drawing.Size(175, 113);
+			this.skinnedGroupBox1.TabIndex = 39;
+			this.skinnedGroupBox1.TabStop = false;
+			this.skinnedGroupBox1.Text = "Case";
+			// 
+			// txtFolder
+			// 
+			this.txtFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtFolder.BackColor = System.Drawing.Color.White;
+			this.txtFolder.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtFolder.ForeColor = System.Drawing.Color.Black;
+			this.txtFolder.Location = new System.Drawing.Point(46, 64);
+			this.txtFolder.Name = "txtFolder";
+			this.txtFolder.Size = new System.Drawing.Size(123, 20);
+			this.txtFolder.TabIndex = 70;
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.skinnedLabel1.AutoSize = true;
+			this.skinnedLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.skinnedLabel1.Location = new System.Drawing.Point(6, 67);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(39, 13);
+			this.skinnedLabel1.TabIndex = 69;
+			this.skinnedLabel1.Text = "Folder:";
+			// 
+			// cmdColorCode
+			// 
+			this.cmdColorCode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdColorCode.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.cmdColorCode.Location = new System.Drawing.Point(146, 85);
+			this.cmdColorCode.Name = "cmdColorCode";
+			this.cmdColorCode.Size = new System.Drawing.Size(23, 23);
+			this.cmdColorCode.TabIndex = 68;
+			this.toolTip1.SetToolTip(this.cmdColorCode, "Set label color");
+			this.cmdColorCode.UseVisualStyleBackColor = true;
+			this.cmdColorCode.Click += new System.EventHandler(this.cmdColorCode_Click);
+			// 
+			// txtLabel
+			// 
+			this.txtLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtLabel.BackColor = System.Drawing.Color.White;
+			this.txtLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtLabel.ForeColor = System.Drawing.Color.Black;
+			this.txtLabel.Location = new System.Drawing.Point(46, 87);
+			this.txtLabel.Name = "txtLabel";
+			this.txtLabel.Size = new System.Drawing.Size(94, 20);
+			this.txtLabel.TabIndex = 67;
+			// 
+			// label4
+			// 
+			this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.label4.AutoSize = true;
+			this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label4.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label4.Location = new System.Drawing.Point(6, 90);
+			this.label4.Name = "label4";
+			this.label4.Size = new System.Drawing.Size(36, 13);
+			this.label4.TabIndex = 66;
+			this.label4.Text = "Label:";
+			// 
+			// label34
+			// 
+			this.label34.AutoSize = true;
+			this.label34.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label34.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label34.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label34.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label34.Location = new System.Drawing.Point(6, 24);
+			this.label34.Name = "label34";
+			this.label34.Size = new System.Drawing.Size(34, 13);
+			this.label34.TabIndex = 63;
+			this.label34.Text = "Type:";
+			// 
+			// cboCaseTags
+			// 
+			this.cboCaseTags.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.cboCaseTags.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboCaseTags.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboCaseTags.BackColor = System.Drawing.Color.White;
+			this.cboCaseTags.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboCaseTags.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboCaseTags.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboCaseTags.FormattingEnabled = true;
+			this.cboCaseTags.Location = new System.Drawing.Point(46, 21);
+			this.cboCaseTags.Name = "cboCaseTags";
+			this.cboCaseTags.SelectedIndex = -1;
+			this.cboCaseTags.SelectedItem = null;
+			this.cboCaseTags.Size = new System.Drawing.Size(123, 21);
+			this.cboCaseTags.Sorted = false;
+			this.cboCaseTags.TabIndex = 64;
+			this.cboCaseTags.SelectedIndexChanged += new System.EventHandler(this.cboCaseTags_SelectedIndexChanged);
+			// 
+			// lblHelpText
+			// 
+			this.lblHelpText.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.lblHelpText.AutoEllipsis = true;
+			this.lblHelpText.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblHelpText.ForeColor = System.Drawing.Color.Black;
+			this.lblHelpText.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblHelpText.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblHelpText.Location = new System.Drawing.Point(6, 45);
+			this.lblHelpText.Name = "lblHelpText";
+			this.lblHelpText.Size = new System.Drawing.Size(163, 13);
+			this.lblHelpText.TabIndex = 65;
+			this.lblHelpText.Text = "Help Text";
+			// 
+			// groupBox3
+			// 
+			this.groupBox3.Controls.Add(this.gridStages);
+			this.groupBox3.Location = new System.Drawing.Point(6, 6);
+			this.groupBox3.Name = "groupBox3";
+			this.groupBox3.Size = new System.Drawing.Size(467, 113);
+			this.groupBox3.TabIndex = 37;
+			this.groupBox3.TabStop = false;
+			// 
+			// gridStages
+			// 
+			this.gridStages.AutoSize = true;
+			this.gridStages.ColumnHeaderHeight = 72;
+			this.gridStages.Location = new System.Drawing.Point(6, 4);
+			this.gridStages.Name = "gridStages";
+			this.gridStages.Size = new System.Drawing.Size(72, 102);
+			this.gridStages.TabIndex = 37;
+			// 
 			// tabTags
 			// 
+			this.tabTags.BackColor = System.Drawing.Color.White;
 			this.tabTags.Controls.Add(this.lstRemoveTags);
 			this.tabTags.Controls.Add(this.lstAddTags);
 			this.tabTags.Controls.Add(this.label3);
 			this.tabTags.Controls.Add(this.label2);
+			this.tabTags.ForeColor = System.Drawing.Color.Black;
 			this.tabTags.Location = new System.Drawing.Point(4, 22);
 			this.tabTags.Name = "tabTags";
 			this.tabTags.Padding = new System.Windows.Forms.Padding(3);
-			this.tabTags.Size = new System.Drawing.Size(662, 347);
+			this.tabTags.Size = new System.Drawing.Size(657, 330);
 			this.tabTags.TabIndex = 1;
 			this.tabTags.Text = "Tags";
 			// 
@@ -237,7 +452,7 @@
 			this.lstRemoveTags.Name = "lstRemoveTags";
 			this.lstRemoveTags.RecordType = null;
 			this.lstRemoveTags.SelectedItems = new string[0];
-			this.lstRemoveTags.Size = new System.Drawing.Size(308, 322);
+			this.lstRemoveTags.Size = new System.Drawing.Size(303, 305);
 			this.lstRemoveTags.TabIndex = 3;
 			// 
 			// lstAddTags
@@ -248,12 +463,16 @@
 			this.lstAddTags.Name = "lstAddTags";
 			this.lstAddTags.RecordType = null;
 			this.lstAddTags.SelectedItems = new string[0];
-			this.lstAddTags.Size = new System.Drawing.Size(336, 322);
+			this.lstAddTags.Size = new System.Drawing.Size(336, 305);
 			this.lstAddTags.TabIndex = 2;
 			// 
 			// label3
 			// 
 			this.label3.AutoSize = true;
+			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label3.ForeColor = System.Drawing.Color.Black;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label3.Location = new System.Drawing.Point(345, 3);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(158, 13);
@@ -263,41 +482,85 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.Black;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label2.Location = new System.Drawing.Point(3, 3);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(137, 13);
 			this.label2.TabIndex = 0;
 			this.label2.Text = "Tags to Add with This Case";
 			// 
+			// stripCase
+			// 
+			this.stripCase.AddCaption = null;
+			this.stripCase.Dock = System.Windows.Forms.DockStyle.Top;
+			this.stripCase.Location = new System.Drawing.Point(0, 0);
+			this.stripCase.Margin = new System.Windows.Forms.Padding(0);
+			this.stripCase.Name = "stripCase";
+			this.stripCase.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
+			this.stripCase.ShowAddButton = false;
+			this.stripCase.ShowCloseButton = false;
+			this.stripCase.Size = new System.Drawing.Size(670, 28);
+			this.stripCase.StartMargin = 5;
+			this.stripCase.TabControl = this.tabCase;
+			this.stripCase.TabIndex = 97;
+			this.stripCase.TabMargin = 5;
+			this.stripCase.TabPadding = 10;
+			this.stripCase.TabSize = -1;
+			this.stripCase.TabType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.stripCase.Vertical = false;
+			// 
+			// containerDialogue
+			// 
+			this.containerDialogue.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.containerDialogue.Controls.Add(this.tabs);
+			this.containerDialogue.Location = new System.Drawing.Point(0, 23);
+			this.containerDialogue.Name = "containerDialogue";
+			this.containerDialogue.PanelType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.containerDialogue.Size = new System.Drawing.Size(670, 267);
+			this.containerDialogue.TabIndex = 97;
+			this.containerDialogue.TabSide = Desktop.Skinning.TabSide.Top;
+			// 
 			// tabs
 			// 
+			this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
 			this.tabs.Controls.Add(this.tabDialogue);
 			this.tabs.Controls.Add(this.tabNotes);
-			this.tabs.Dock = System.Windows.Forms.DockStyle.Fill;
-			this.tabs.Location = new System.Drawing.Point(0, 0);
+			this.tabs.Location = new System.Drawing.Point(1, 0);
 			this.tabs.Margin = new System.Windows.Forms.Padding(0);
 			this.tabs.Name = "tabs";
 			this.tabs.SelectedIndex = 0;
-			this.tabs.Size = new System.Drawing.Size(670, 279);
+			this.tabs.Size = new System.Drawing.Size(668, 266);
 			this.tabs.TabIndex = 95;
 			// 
 			// tabDialogue
 			// 
+			this.tabDialogue.BackColor = System.Drawing.Color.White;
 			this.tabDialogue.Controls.Add(this.lblAvailableVars);
 			this.tabDialogue.Controls.Add(this.cmdCopyAll);
 			this.tabDialogue.Controls.Add(this.cmdPasteAll);
 			this.tabDialogue.Controls.Add(this.gridDialogue);
+			this.tabDialogue.ForeColor = System.Drawing.Color.Black;
 			this.tabDialogue.Location = new System.Drawing.Point(4, 22);
 			this.tabDialogue.Name = "tabDialogue";
 			this.tabDialogue.Padding = new System.Windows.Forms.Padding(3);
-			this.tabDialogue.Size = new System.Drawing.Size(662, 253);
+			this.tabDialogue.Size = new System.Drawing.Size(660, 240);
 			this.tabDialogue.TabIndex = 0;
 			this.tabDialogue.Text = "Dialogue";
-			this.tabDialogue.UseVisualStyleBackColor = true;
 			// 
 			// lblAvailableVars
 			// 
 			this.lblAvailableVars.AutoSize = true;
+			this.lblAvailableVars.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblAvailableVars.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.lblAvailableVars.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.lblAvailableVars.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
 			this.lblAvailableVars.Location = new System.Drawing.Point(2, 8);
 			this.lblAvailableVars.Name = "lblAvailableVars";
 			this.lblAvailableVars.Size = new System.Drawing.Size(158, 13);
@@ -307,9 +570,13 @@
 			// cmdCopyAll
 			// 
 			this.cmdCopyAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdCopyAll.Location = new System.Drawing.Point(500, 3);
+			this.cmdCopyAll.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCopyAll.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCopyAll.Flat = true;
+			this.cmdCopyAll.ForeColor = System.Drawing.Color.Blue;
+			this.cmdCopyAll.Location = new System.Drawing.Point(496, 3);
 			this.cmdCopyAll.Name = "cmdCopyAll";
-			this.cmdCopyAll.Size = new System.Drawing.Size(75, 23);
+			this.cmdCopyAll.Size = new System.Drawing.Size(77, 23);
 			this.cmdCopyAll.TabIndex = 39;
 			this.cmdCopyAll.Text = "Copy All";
 			this.cmdCopyAll.UseVisualStyleBackColor = true;
@@ -318,9 +585,13 @@
 			// cmdPasteAll
 			// 
 			this.cmdPasteAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdPasteAll.Location = new System.Drawing.Point(581, 3);
+			this.cmdPasteAll.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdPasteAll.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdPasteAll.Flat = true;
+			this.cmdPasteAll.ForeColor = System.Drawing.Color.Blue;
+			this.cmdPasteAll.Location = new System.Drawing.Point(579, 3);
 			this.cmdPasteAll.Name = "cmdPasteAll";
-			this.cmdPasteAll.Size = new System.Drawing.Size(75, 23);
+			this.cmdPasteAll.Size = new System.Drawing.Size(77, 23);
 			this.cmdPasteAll.TabIndex = 40;
 			this.cmdPasteAll.Text = "Paste All";
 			this.cmdPasteAll.UseVisualStyleBackColor = true;
@@ -334,28 +605,32 @@
 			this.gridDialogue.Location = new System.Drawing.Point(3, 32);
 			this.gridDialogue.Name = "gridDialogue";
 			this.gridDialogue.ReadOnly = false;
-			this.gridDialogue.Size = new System.Drawing.Size(653, 218);
+			this.gridDialogue.Size = new System.Drawing.Size(651, 205);
 			this.gridDialogue.TabIndex = 42;
 			this.gridDialogue.KeyDown += new System.EventHandler<System.Windows.Forms.KeyEventArgs>(this.gridDialogue_KeyDown);
 			this.gridDialogue.HighlightRow += new System.EventHandler<int>(this.gridDialogue_HighlightRow);
 			// 
 			// tabNotes
 			// 
+			this.tabNotes.BackColor = System.Drawing.Color.White;
 			this.tabNotes.Controls.Add(this.txtNotes);
 			this.tabNotes.Controls.Add(this.label1);
+			this.tabNotes.ForeColor = System.Drawing.Color.Black;
 			this.tabNotes.Location = new System.Drawing.Point(4, 22);
 			this.tabNotes.Name = "tabNotes";
 			this.tabNotes.Padding = new System.Windows.Forms.Padding(3);
-			this.tabNotes.Size = new System.Drawing.Size(662, 253);
+			this.tabNotes.Size = new System.Drawing.Size(660, 240);
 			this.tabNotes.TabIndex = 1;
 			this.tabNotes.Text = "Notes";
-			this.tabNotes.UseVisualStyleBackColor = true;
 			// 
 			// txtNotes
 			// 
 			this.txtNotes.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtNotes.BackColor = System.Drawing.Color.White;
+			this.txtNotes.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtNotes.ForeColor = System.Drawing.Color.Black;
 			this.txtNotes.Location = new System.Drawing.Point(5, 19);
 			this.txtNotes.Multiline = true;
 			this.txtNotes.Name = "txtNotes";
@@ -366,6 +641,10 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(6, 3);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(413, 13);
@@ -373,41 +652,30 @@
 			this.label1.Text = "Jot down any personal notes about this case here. These notes won\'t appear in gam" +
     "e.";
 			// 
-			// lblHelpText
-			// 
-			this.lblHelpText.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblHelpText.Location = new System.Drawing.Point(217, 0);
-			this.lblHelpText.Name = "lblHelpText";
-			this.lblHelpText.Size = new System.Drawing.Size(278, 38);
-			this.lblHelpText.TabIndex = 65;
-			this.lblHelpText.Text = "Help Text";
-			// 
-			// cboCaseTags
-			// 
-			this.cboCaseTags.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
-			this.cboCaseTags.FormattingEnabled = true;
-			this.cboCaseTags.Location = new System.Drawing.Point(36, 0);
-			this.cboCaseTags.Name = "cboCaseTags";
-			this.cboCaseTags.Size = new System.Drawing.Size(175, 21);
-			this.cboCaseTags.TabIndex = 64;
-			// 
-			// label34
-			// 
-			this.label34.AutoSize = true;
-			this.label34.Location = new System.Drawing.Point(1, 3);
-			this.label34.Name = "label34";
-			this.label34.Size = new System.Drawing.Size(34, 13);
-			this.label34.TabIndex = 63;
-			this.label34.Text = "Type:";
+			// stripTabs
+			// 
+			this.stripTabs.AddCaption = null;
+			this.stripTabs.Dock = System.Windows.Forms.DockStyle.Top;
+			this.stripTabs.Location = new System.Drawing.Point(0, 0);
+			this.stripTabs.Margin = new System.Windows.Forms.Padding(0);
+			this.stripTabs.Name = "stripTabs";
+			this.stripTabs.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.stripTabs.ShowAddButton = false;
+			this.stripTabs.ShowCloseButton = false;
+			this.stripTabs.Size = new System.Drawing.Size(670, 23);
+			this.stripTabs.StartMargin = 5;
+			this.stripTabs.TabControl = this.tabs;
+			this.stripTabs.TabIndex = 96;
+			this.stripTabs.TabMargin = 5;
+			this.stripTabs.TabPadding = 10;
+			this.stripTabs.TabSize = -1;
+			this.stripTabs.TabType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.stripTabs.Vertical = false;
 			// 
 			// CaseControl
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.lblHelpText);
-			this.Controls.Add(this.cboCaseTags);
-			this.Controls.Add(this.label34);
 			this.Controls.Add(this.splitCase);
 			this.Name = "CaseControl";
 			this.Size = new System.Drawing.Size(670, 680);
@@ -416,54 +684,70 @@
 			this.splitCase.Panel2.ResumeLayout(false);
 			((System.ComponentModel.ISupportInitialize)(this.splitCase)).EndInit();
 			this.splitCase.ResumeLayout(false);
+			this.skinnedPanel1.ResumeLayout(false);
 			this.tabCase.ResumeLayout(false);
 			this.tabConditions.ResumeLayout(false);
+			this.tabConditions.PerformLayout();
+			this.tabsConditions.ResumeLayout(false);
+			this.skinnedPanel2.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.valPriority)).EndInit();
+			this.skinnedGroupBox1.ResumeLayout(false);
+			this.skinnedGroupBox1.PerformLayout();
 			this.groupBox3.ResumeLayout(false);
 			this.groupBox3.PerformLayout();
-			this.grpConditions.ResumeLayout(false);
-			this.grpConditions.PerformLayout();
-			((System.ComponentModel.ISupportInitialize)(this.valPriority)).EndInit();
 			this.tabTags.ResumeLayout(false);
 			this.tabTags.PerformLayout();
+			this.containerDialogue.ResumeLayout(false);
 			this.tabs.ResumeLayout(false);
 			this.tabDialogue.ResumeLayout(false);
 			this.tabDialogue.PerformLayout();
 			this.tabNotes.ResumeLayout(false);
 			this.tabNotes.PerformLayout();
 			this.ResumeLayout(false);
-			this.PerformLayout();
 
 		}
 
 		#endregion
 
 		private System.Windows.Forms.SplitContainer splitCase;
-		private System.Windows.Forms.TabControl tabCase;
+		private Desktop.Skinning.SkinnedTabControl tabCase;
 		private System.Windows.Forms.TabPage tabConditions;
-		private System.Windows.Forms.GroupBox groupBox3;
-		private System.Windows.Forms.CheckBox chkSelectAll;
-		private System.Windows.Forms.FlowLayoutPanel flowStageChecks;
-		private System.Windows.Forms.GroupBox grpConditions;
-		private System.Windows.Forms.NumericUpDown valPriority;
-		private System.Windows.Forms.Label label73;
+		private Desktop.Skinning.SkinnedGroupBox groupBox3;
+		private Desktop.Skinning.SkinnedNumericUpDown valPriority;
+		private Desktop.Skinning.SkinnedLabel label73;
 		private Desktop.CommonControls.PropertyTable tableConditions;
 		private System.Windows.Forms.TabPage tabTags;
 		private RecordSelectBox lstRemoveTags;
 		private RecordSelectBox lstAddTags;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.TabControl tabs;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedTabControl tabs;
 		private System.Windows.Forms.TabPage tabDialogue;
-		private System.Windows.Forms.Label lblAvailableVars;
-		private System.Windows.Forms.Button cmdCopyAll;
-		private System.Windows.Forms.Button cmdPasteAll;
+		private Desktop.Skinning.SkinnedLabel lblAvailableVars;
+		private Desktop.Skinning.SkinnedButton cmdCopyAll;
+		private Desktop.Skinning.SkinnedButton cmdPasteAll;
 		private DialogueGrid gridDialogue;
 		private System.Windows.Forms.TabPage tabNotes;
-		private System.Windows.Forms.TextBox txtNotes;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label lblHelpText;
-		private System.Windows.Forms.ComboBox cboCaseTags;
-		private System.Windows.Forms.Label label34;
+		private Desktop.Skinning.SkinnedTextBox txtNotes;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel lblHelpText;
+		private Desktop.Skinning.SkinnedComboBox cboCaseTags;
+		private Desktop.Skinning.SkinnedLabel label34;
 		private System.Windows.Forms.ToolTip toolTip1;
+		private Desktop.Skinning.SkinnedLabel label4;
+		private Desktop.Skinning.SkinnedTextBox txtLabel;
+		private StageGrid gridStages;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox1;
+		private Desktop.Skinning.SkinnedTabStrip stripTabs;
+		private Desktop.Skinning.SkinnedTabStrip stripCase;
+		private Desktop.Skinning.SkinnedPanel containerDialogue;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private System.Windows.Forms.Button cmdColorCode;
+		private Desktop.Skinning.SkinnedTextBox txtFolder;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
+		private Desktop.Skinning.SkinnedTabStrip stripConditions;
+		private Desktop.Skinning.SkinnedTabControl tabsConditions;
+		private System.Windows.Forms.TabPage tabPage1;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel2;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/CaseControl.cs b/editor source/SPNATI Character Editor/Controls/CaseControl.cs
index 4e8e33f0f70e1b1422c02342f60706b38a837a11..43001c5f8655e7a1221c68d29bc3e39933d821cd 100644
--- a/editor source/SPNATI Character Editor/Controls/CaseControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/CaseControl.cs	
@@ -1,13 +1,15 @@
 using Desktop;
 using Desktop.CommonControls;
+using Desktop.Skinning;
 using SPNATI_Character_Editor.Forms;
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Controls
 {
-	public partial class CaseControl : UserControl, IMacroEditor
+	public partial class CaseControl : UserControl, IMacroEditor, ISkinnedPanel, ISkinControl
 	{
 		private const string FavoriteConditionsSetting = "FavoritedConditions";
 
@@ -27,12 +29,18 @@ namespace SPNATI_Character_Editor.Controls
 			InitializeComponent();
 		}
 
+		public void OnUpdateSkin(Skin skin)
+		{
+			BackColor = skin.Background.GetColor(VisualState.Normal, false, Enabled);
+		}
+
 		private void CaseControl_Load(object sender, EventArgs e)
 		{
 			tableConditions.RecordFilter = FilterTargets;
 			lstAddTags.RecordType = typeof(Tag);
 			lstRemoveTags.RecordType = typeof(Tag);
-			//SetupMessageHandlers();
+			gridStages.CheckedChanged += Check_CheckedChanged;
+			gridStages.LayerSelected += GridStages_LayerSelected;
 			gridDialogue.TextUpdated += GridDialogue_TextUpdated;
 		}
 
@@ -50,17 +58,45 @@ namespace SPNATI_Character_Editor.Controls
 			CreateStageCheckboxes();
 		}
 
+		public Case GetCase()
+		{
+			return _selectedCase;
+		}
+
 		public void SetCase(Stage stage, Case workingCase)
 		{
+			if (_selectedCase != null)
+			{
+				_selectedCase.AlternativeConditions.CollectionChanged -= AlternativeConditions_CollectionChanged;
+				tabsConditions.Selected -= TabsConditions_Selected;
+				tabsConditions.SelectedIndexChanged -= tabsConditions_SelectedIndexChanged;
+				tabsConditions.SelectedIndex = 0;
+				for (int i = tabsConditions.TabPages.Count - 1; i > 0; i--)
+				{
+					tabsConditions.TabPages.RemoveAt(i);
+				}
+			}
 			_selectedStage = stage;
 			_selectedCase = workingCase;
 			if (_selectedCase != null)
 			{
-				grpConditions.Enabled = true;
+				_selectedCase.AlternativeConditions.CollectionChanged += AlternativeConditions_CollectionChanged;
+				tabConditions.Enabled = true;
+				foreach (Case alternative in _selectedCase.AlternativeConditions)
+				{
+					AddAlternateTab();
+				}
+				tabsConditions.SelectedIndexChanged += tabsConditions_SelectedIndexChanged;
+				tabsConditions.Selected += TabsConditions_Selected;
 			}
 			PopulateCase();
 		}
 
+		private void TabsConditions_Selected(object sender, TabControlEventArgs e)
+		{
+			
+		}
+
 		public void UpdateStages()
 		{
 			if (_character == null) { return; }
@@ -107,35 +143,28 @@ namespace SPNATI_Character_Editor.Controls
 			if (_populatingCase)
 				return;
 			_populatingCase = true;
-			UpdateCheckAllState();
-			gridDialogue.UpdateAvailableImagesForCase(GetSelectedStages(), true);
+			HashSet<int> stages = GetSelectedStages();
+			UpdatePreviewStage(stages, -1);
 			_populatingCase = false;
 		}
 
-		/// <summary>
-		/// Checks or unchecks all stages besides the current stage
-		/// </summary>
-		/// <param name="sender"></param>
-		/// <param name="e"></param>
-		private void chkSelectAll_CheckedChanged(object sender, EventArgs e)
+		private void GridStages_LayerSelected(object sender, int layer)
 		{
-			if (_populatingCase)
-				return;
-			int currentStage = _selectedStage == null ? 0 : _selectedStage.Id;
-			bool newState = chkSelectAll.Checked;
-			_populatingCase = true;
-			for (int i = 0; i < flowStageChecks.Controls.Count; i++)
+			UpdatePreviewStage(GetSelectedStages(), layer);
+		}
+
+		private void UpdatePreviewStage(HashSet<int> stages, int desiredStage)
+		{
+			if (_selectedStage != null && stages.Count > 0)
 			{
-				if (i == currentStage)
-					continue;
-				CheckBox box = flowStageChecks.Controls[i] as CheckBox;
-				if (box != null && box.Enabled)
+				if (desiredStage == -1)
 				{
-					box.Checked = newState;
+					desiredStage = stages.Min();
 				}
+				_selectedStage = new Stage(desiredStage);
+				gridDialogue.SetStage(_selectedStage, stages);
+				gridStages.SetPreviewStage(_selectedStage.Id);
 			}
-			_populatingCase = false;
-			gridDialogue.UpdateAvailableImagesForCase(GetSelectedStages(), true);
 		}
 
 		/// <summary>
@@ -228,24 +257,25 @@ namespace SPNATI_Character_Editor.Controls
 
 			PopulateStageCheckboxes();
 
-			int stageId = _selectedStage == null ? 0 : _selectedStage.Id;
 			Trigger caseTrigger = TriggerDatabase.GetTrigger(stageCase.Tag);
 
 			#region Case-wide settings
 			//Tag combo box
 			cboCaseTags.Items.Clear();
+			Trigger currentTrigger = TriggerDatabase.GetTrigger(_selectedCase.Tag);
 			if (_selectedStage != null)
 			{
 				Trigger selection = null;
 				foreach (string tag in TriggerDatabase.GetTags())
 				{
-					if (TriggerDatabase.UsedInStage(tag, _character, stageId))
+					Trigger t = TriggerDatabase.GetTrigger(tag);
+					if (currentTrigger.HasTarget && currentTrigger.HasTarget != t.HasTarget)
 					{
-						Trigger t = new Trigger(tag, TriggerDatabase.GetLabel(tag));
-						if (tag == _selectedCase.Tag)
-							selection = t;
-						cboCaseTags.Items.Add(t);
+						continue;
 					}
+					if (tag == _selectedCase.Tag)
+						selection = t;
+					cboCaseTags.Items.Add(t);
 				}
 				cboCaseTags.SelectedItem = selection;
 				cboCaseTags.Enabled = true;
@@ -273,6 +303,22 @@ namespace SPNATI_Character_Editor.Controls
 			#endregion
 
 			txtNotes.Text = _editorData.GetNote(_selectedCase);
+			CaseLabel label = _editorData.GetLabel(_selectedCase);
+			txtFolder.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
+			txtFolder.AutoCompleteSource = AutoCompleteSource.CustomSource;
+			txtFolder.AutoCompleteCustomSource = _editorData.Folders;
+			if (label == null)
+			{
+				txtLabel.Text = null;
+				txtFolder.Text = null;
+				SetColorButton(null);
+			}
+			else
+			{
+				txtLabel.Text = label.Text;
+				txtFolder.Text = label.Folder;
+				SetColorButton(label.ColorCode);
+			}
 
 			if (caseTrigger.HasTarget)
 			{
@@ -283,8 +329,7 @@ namespace SPNATI_Character_Editor.Controls
 				tableConditions.RecordFilter = FilterTargets;
 			}
 			bool firstPopulation = (tableConditions.Data == null);
-			tableConditions.Data = _selectedCase;
-			AddSpeedButtons();
+			PopulateConditionTable(_selectedCase);
 
 			if (firstPopulation)
 			{
@@ -308,57 +353,61 @@ namespace SPNATI_Character_Editor.Controls
 
 			var stages = GetSelectedStages();
 			gridDialogue.SetData(_character, _selectedStage, _selectedCase, stages, _imageLibrary);
-			GetSelectedStages();
 
 			PopulateTagsTab();
 
 			_populatingCase = false;
-			//HighlightRow(0);
 		}
 
-		private void AddSpeedButtons()
+		private void PopulateConditionTable(Case workingCase)
 		{
-			if (_selectedCase == null) { return; }
-			Trigger caseTrigger = TriggerDatabase.GetTrigger(_selectedCase.Tag);
-			tableConditions.AddSpeedButton("Game", "Background", (data) => { return AddVariableTest("~background~", data); });
-			tableConditions.AddSpeedButton("Game", "Inside/Outside", (data) => { return AddVariableTest("~background.location~", data); });
+			tableConditions.Data = workingCase;
+			AddSpeedButtons(tableConditions, workingCase?.Tag);
+		}
+
+		public static void AddSpeedButtons(PropertyTable table, string tag)
+		{
+			if (tag == null) { return; }
+			Trigger caseTrigger = TriggerDatabase.GetTrigger(tag);
+
+			//Game-wide
+			table.AddSpeedButton("Game", "Background", (data) => { return AddVariableTest("~background~", data); });
+			table.AddSpeedButton("Game", "Inside/Outside", (data) => { return AddVariableTest("~background.location~", data); });
+
+			//Player variables
+
+			table.AddSpeedButton("Player", "Collectible (+)", (data) => { return AddVariableTest("~_.collectible.*~", data); });
+			table.AddSpeedButton("Player", "Collectible (Counter) (+)", (data) => { return AddVariableTest("~_.collectible.*.counter~", data); });
+			table.AddSpeedButton("Player", "Costume (+)", (data) => { return AddVariableTest("~_.costume~", data); });
+			table.AddSpeedButton("Player", "Largest Lead (+)", (data) => { return AddVariableTest("~_.biggestlead~", data); });
+			table.AddSpeedButton("Player", "Layer Difference (+)", (data) => { return AddVariableTest("~_.diff~", data); });
+			table.AddSpeedButton("Player", "Marker (+)", (data) => { return AddVariableTest("~_.marker.*~", data); });
+			table.AddSpeedButton("Player", "Marker (Persistent) (+)", (data) => { return AddVariableTest("~_.persistent.*~", data); });
+			table.AddSpeedButton("Player", "Place (+)", (data) => { return AddVariableTest("~_.lead~", data); });
+			table.AddSpeedButton("Player", "Relative Position (+)", (data) => { return AddVariableTest("~_.position~", data); });
+			table.AddSpeedButton("Player", "Slot (+)", (data) => { return AddVariableTest("~_.slot~", data); });
+			table.AddSpeedButton("Player", "Stage (+)", (data) => { return AddVariableTest("~_.stage~", data); });
+			table.AddSpeedButton("Player", "Tag (+)", (data) => { return AddVariableTest("~_.tag.*~", data); });
+
+			//Table-wide
+			table.AddSpeedButton("Table", "Human Name", (data) => { return AddVariableTest("~player~", data); });
 			if (caseTrigger.AvailableVariables.Contains("cards"))
 			{
-				tableConditions.AddSpeedButton("Self", "Cards Exchanged", (data) => { return AddVariableTest("~cards~", data); });
+				table.AddSpeedButton("Self", "Cards Exchanged", (data) => { return AddVariableTest("~cards~", data); });
 			}
-			tableConditions.AddSpeedButton("Self", "Collectible", (data) => { return AddVariableTest("~collectible.*~", data); });
-			tableConditions.AddSpeedButton("Self", "Collectible (Counter)", (data) => { return AddVariableTest("~collectible.*.counter~", data); });
-			tableConditions.AddSpeedButton("Self", "Costume", (data) => { return AddVariableTest("~self.costume~", data); });
-			tableConditions.AddSpeedButton("Self", "Marker (Persistent) (+)", (data) => { return AddVariableTest("~persistent.*~", data); });
-			tableConditions.AddSpeedButton("Self", "Slot", (data) => { return AddVariableTest("~self.slot~", data); });
-			tableConditions.AddSpeedButton("Self", "Tag", (data) => { return AddVariableTest("~self.tag.*~", data); });
 			if (caseTrigger.HasTarget)
 			{
 				if (caseTrigger.AvailableVariables.Contains("clothing"))
 				{
-					tableConditions.AddSpeedButton("Clothing", "Clothing Position", (data) => { return AddVariableTest("~clothing.position~", data); });
-					tableConditions.AddSpeedButton("Clothing", "Clothing Type", (data) => { return AddVariableTest("~clothing.type~", data); });
+					table.AddSpeedButton("Clothing", "Clothing Position", (data) => { return AddVariableTest("~clothing.position~", data); });
+					table.AddSpeedButton("Clothing", "Clothing Type", (data) => { return AddVariableTest("~clothing.type~", data); });
 				}
-				tableConditions.AddSpeedButton("Target", "Collectible (+)", (data) => { return AddVariableTest("~target.collectible.*~", data); });
-				tableConditions.AddSpeedButton("Target", "Collectible (Counter) (+)", (data) => { return AddVariableTest("~target.collectible.*.counter~", data); });
-				tableConditions.AddSpeedButton("Target", "Costume", (data) => { return AddVariableTest("~target.costume~", data); });
-				tableConditions.AddSpeedButton("Target", "Gender", (data) => { return AddVariableTest("~target.gender~", data); });
-				tableConditions.AddSpeedButton("Target", "Marker (Persistent) (+)", (data) => { return AddVariableTest("~target.persistent.*~", data); });
-				tableConditions.AddSpeedButton("Target", "Position", (data) => { return AddVariableTest("~target.position~", data); });
-				tableConditions.AddSpeedButton("Target", "Size", (data) => { return AddVariableTest("~target.size~", data); });
-				tableConditions.AddSpeedButton("Target", "Slot", (data) => { return AddVariableTest("~target.slot~", data); });
-				tableConditions.AddSpeedButton("Target", "Tag (+)", (data) => { return AddVariableTest("~target.tag.*~", data); });
-			}
-			tableConditions.AddSpeedButton("Also Playing", "Collectible (+)", (data) => { return AddVariableTest("~_.collectible.*~", data); });
-			tableConditions.AddSpeedButton("Also Playing", "Collectible (Counter) (+)", (data) => { return AddVariableTest("~_.collectible.*.counter~", data); });
-			tableConditions.AddSpeedButton("Also Playing", "Costume", (data) => { return AddVariableTest("~_.costume~", data); });
-			tableConditions.AddSpeedButton("Also Playing", "Marker (Persistent) (+)", (data) => { return AddVariableTest("~_.persistent.*~", data); });
-			tableConditions.AddSpeedButton("Also Playing", "Position", (data) => { return AddVariableTest("~_.position~", data); });
-			tableConditions.AddSpeedButton("Also Playing", "Slot", (data) => { return AddVariableTest("~_.slot~", data); });
-			tableConditions.AddSpeedButton("Also Playing", "Tag (+)", (data) => { return AddVariableTest("~_.tag.*~", data); });
-		}
-
-		private string AddVariableTest(string variable, object data)
+				table.AddSpeedButton("Target", "Gender", (data) => { return AddVariableTest("~target.gender~", data); });
+				table.AddSpeedButton("Target", "Size", (data) => { return AddVariableTest("~target.size~", data); });
+			}
+		}
+
+		private static string AddVariableTest(string variable, object data)
 		{
 			Case theCase = data as Case;
 			theCase.Expressions.Add(new ExpressionTest(variable, ""));
@@ -378,10 +427,9 @@ namespace SPNATI_Character_Editor.Controls
 		private HashSet<int> GetSelectedStages()
 		{
 			HashSet<int> selectedStages = new HashSet<int>();
-			for (int i = 0; i < flowStageChecks.Controls.Count; i++)
+			for (int i = 0; i < _character.Layers + Clothing.ExtraStages; i++)
 			{
-				CheckBox box = flowStageChecks.Controls[i] as CheckBox;
-				if (box.Checked)
+				if (gridStages.GetChecked(i))
 				{
 					selectedStages.Add(i);
 				}
@@ -394,28 +442,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		private void CreateStageCheckboxes()
 		{
-			//Stage checkmarks
-			for (int i = 0; i < flowStageChecks.Controls.Count; i++)
-			{
-				CheckBox box = flowStageChecks.Controls[i] as CheckBox;
-				if (box != null)
-				{
-					box.CheckedChanged -= Check_CheckedChanged;
-				}
-			}
-			flowStageChecks.Controls.Clear();
-			int layers = _character.Layers + 3;
-			for (int i = 0; i < layers; i++)
-			{
-				StageName stage = _character.LayerToStageName(i);
-				CheckBox check = new CheckBox();
-				check.CheckedChanged += Check_CheckedChanged;
-				check.Tag = stage;
-				check.Text = string.Format("{0} ({1})", stage.DisplayName, stage.Id);
-				check.Width = 180;
-				check.Margin = new Padding(0);
-				flowStageChecks.Controls.Add(check);
-			}
+			gridStages.SetData(_character, null, -1);
 		}
 
 		private bool FilterTargets(PropertyRecord record)
@@ -436,36 +463,39 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		/// <param name="switchingCases">True when saving within the context of switching selected cases</param>
 		/// <returns>True if cases were changed in such a way that the dialogue tree needs to be regenerated</returns>
-		private bool SaveCase()
+		private void SaveCase()
 		{
 			if (_selectedCase == null)
-				return false;
+			{
+				return;
+			}
 
 			SaveNotes();
-			bool needRegeneration = false;
 			var c = _selectedCase;
 			if (c.Tag != Trigger.StartTrigger)
 			{
-				string newTag = GUIHelper.ReadComboBox(cboCaseTags);
-				if (newTag != c.Tag)
-					needRegeneration = true;
+				string newTag = "";
+				Trigger trigger = cboCaseTags.SelectedItem as Trigger;
+				if (trigger != null)
+				{
+					newTag = trigger.Tag;
+				}
 				c.Tag = newTag;
+				foreach (Case alternate in c.AlternativeConditions)
+				{
+					alternate.Tag = newTag;
+				}
 
 				//Figure out the stages
 				List<int> oldStages = new List<int>();
 				oldStages.AddRange(c.Stages);
 				c.Stages.Clear();
-				for (int i = 0; i < flowStageChecks.Controls.Count; i++)
+				for (int i = 0; i < _character.Layers + Clothing.ExtraStages; i++)
 				{
-					CheckBox box = flowStageChecks.Controls[i] as CheckBox;
-					if (box.Checked && TriggerDatabase.UsedInStage(newTag, _character, i))
+					if (gridStages.GetChecked(i) && TriggerDatabase.UsedInStage(newTag, _character, i))
 					{
 						c.Stages.Add(i);
-						if (!oldStages.Contains(i))
-							needRegeneration = true;
 					}
-					else if (oldStages.Contains(i))
-						needRegeneration = true;
 				}
 
 				tableConditions.Save();
@@ -479,8 +509,6 @@ namespace SPNATI_Character_Editor.Controls
 			SaveTagsTab();
 
 			_character.Behavior.ApplyChanges(_selectedCase);
-
-			return needRegeneration;
 		}
 
 		/// <summary>
@@ -488,44 +516,12 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		private void PopulateStageCheckboxes()
 		{
-			chkSelectAll.Enabled = (_selectedStage != null);
-			for (int i = 0; i < flowStageChecks.Controls.Count; i++)
-			{
-				CheckBox box = flowStageChecks.Controls[i] as CheckBox;
-				if (_selectedCase != null)
-				{
-					box.Enabled = TriggerDatabase.UsedInStage(_selectedCase.Tag, _character, i);
-				}
-				box.Checked = _selectedCase == null ? false : _selectedCase.Stages.Contains(i);
-			}
-			UpdateCheckAllState();
-		}
-
-		/// <summary>
-		/// Updates the Select All checkbox based on the individual stage checkboxes
-		/// </summary>
-		private void UpdateCheckAllState()
-		{
-			bool allChecked = true;
-			bool noneChecked = true;
-			for (int i = 0; i < flowStageChecks.Controls.Count; i++)
-			{
-				CheckBox box = flowStageChecks.Controls[i] as CheckBox;
-				if (_selectedStage != null && _selectedStage.Id != i && box.Enabled)
-				{
-					if (box.Checked)
-						noneChecked = false;
-					else allChecked = false;
-				}
-			}
-			if (chkSelectAll.Enabled)
-			{
-				chkSelectAll.CheckState = allChecked ? CheckState.Checked : noneChecked ? CheckState.Unchecked : CheckState.Indeterminate;
-			}
-			else
+			int stageId = -1;
+			if (_selectedStage != null)
 			{
-				chkSelectAll.Checked = false;
+				stageId = _selectedStage.Id;
 			}
+			gridStages.SetData(_character, _selectedCase, stageId);
 		}
 
 		private void SaveTagsTab()
@@ -562,6 +558,7 @@ namespace SPNATI_Character_Editor.Controls
 				return;
 			}
 			_editorData.SetNote(_selectedCase, txtNotes.Text);
+			_editorData.SetLabel(_selectedCase, txtLabel.Text, cmdColorCode.Tag?.ToString(), txtFolder.Text);
 		}
 
 		#region Macro editing
@@ -578,6 +575,11 @@ namespace SPNATI_Character_Editor.Controls
 			}
 		}
 
+		public SkinnedBackgroundType PanelType
+		{
+			get { return SkinnedBackgroundType.Background; }
+		}
+
 		public string GetHelpText()
 		{
 			return MacroManager.HelpText;
@@ -614,5 +616,96 @@ namespace SPNATI_Character_Editor.Controls
 		{
 			Config.SaveMacros("Case");
 		}
+
+		private void cboCaseTags_SelectedIndexChanged(object sender, EventArgs e)
+		{
+			Trigger tag = cboCaseTags.SelectedItem as Trigger;
+			if (tag != null)
+			{
+				lblHelpText.Text = tag.HelpText;
+			}
+		}
+
+		public void AddSpeedButtons(PropertyTable table)
+		{
+			AddSpeedButtons(table, _selectedCase?.Tag);
+		}
+
+		private void cmdColorCode_Click(object sender, EventArgs e)
+		{
+			ColorCode color = RecordLookup.DoLookup(typeof(ColorCode), "", false, null) as ColorCode;
+			if (color != null)
+			{
+				SetColorButton(color.Key);
+			}
+		}
+
+		private void SetColorButton(string colorCode)
+		{
+			ColorCode code = Definitions.Instance.Get<ColorCode>(colorCode);
+			if (code == null)
+			{
+				cmdColorCode.BackColor = SkinManager.Instance.CurrentSkin.Background.Normal;
+				cmdColorCode.Tag = null;
+			}
+			else
+			{
+				cmdColorCode.BackColor = code.GetColor();
+				cmdColorCode.Tag = colorCode;
+			}
+		}
+
+		private void AlternativeConditions_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+		{
+			switch (e.Action)
+			{
+				case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
+					AddAlternateTab();
+					break;
+				case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
+					tabsConditions.TabPages.RemoveAt(e.OldStartingIndex + 1);
+					for (int i = e.OldStartingIndex + 1; i < tabsConditions.TabPages.Count; i++)
+					{
+						tabsConditions.TabPages[i].Text = "Set " + (i + 1);
+					}
+					break;
+			}
+		}
+
+		private void stripConditions_AddButtonClicked(object sender, EventArgs e)
+		{
+			if (_selectedCase == null) { return; }
+			Case alternate = new Case(_selectedCase.Tag);
+			_selectedCase.AlternativeConditions.Add(alternate);
+			tabsConditions.SelectedIndex = _selectedCase.AlternativeConditions.Count;
+		}
+
+		private void AddAlternateTab()
+		{
+			tabsConditions.TabPages.Add($"Set {(tabsConditions.TabPages.Count + 1)}");
+		}
+
+		private void stripConditions_CloseButtonClicked(object sender, EventArgs e)
+		{
+			if (_selectedCase == null) { return; }
+			int index = tabsConditions.SelectedIndex - 1;
+			if (index >= 0)
+			{
+				_selectedCase.AlternativeConditions.RemoveAt(index);
+				tabsConditions.SelectedIndex = index < tabsConditions.TabPages.Count - 1 ? index + 1 : index;
+			}
+		}
+
+		private void tabsConditions_SelectedIndexChanged(object sender, EventArgs e)
+		{
+			if (_selectedCase == null) { return; }
+			int index = tabsConditions.SelectedIndex;
+			Case desiredCase = _selectedCase;
+			if (index > 0)
+			{
+				desiredCase = _selectedCase.AlternativeConditions[index - 1];
+			}
+			PopulateConditionTable(desiredCase);
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/CharacterImageBox.cs b/editor source/SPNATI Character Editor/Controls/CharacterImageBox.cs
index 93415cbe97aaf52e02ca40c15233db53d420ef62..daeb25218d50f1abe3c3611b8fb71566f835a8e0 100644
--- a/editor source/SPNATI Character Editor/Controls/CharacterImageBox.cs	
+++ b/editor source/SPNATI Character Editor/Controls/CharacterImageBox.cs	
@@ -1,5 +1,6 @@
 using Desktop;
 using Desktop.Messaging;
+using Desktop.Skinning;
 using SPNATI_Character_Editor.EpilogueEditor;
 using System;
 using System.Collections.Generic;
@@ -9,7 +10,7 @@ using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Controls
 {
-	public partial class CharacterImageBox : UserControl
+	public partial class CharacterImageBox : UserControl, ISkinControl
 	{
 		private const int ScreenMargin = 5;
 		private const float TextPercent = 0.2f;
@@ -24,10 +25,10 @@ namespace SPNATI_Character_Editor.Controls
 		private CharacterImage _image;
 		private Mailbox _mailbox;
 		private Image _imageReference;
-		private LivePose _imagePose;
 		private bool _animating;
 		private string _text = null;
 		private float _percent = 0.5f;
+		private List<string> _markers = new List<string>();
 
 		private DateTime _lastTick;
 
@@ -35,6 +36,8 @@ namespace SPNATI_Character_Editor.Controls
 		private Pen _textBorder;
 
 		public Matrix SceneTransform;
+		public LivePose Pose;
+		public bool AutoPlayback = true;
 
 		public CharacterImageBox()
 		{
@@ -44,6 +47,7 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				_mailbox = Shell.Instance.PostOffice.GetMailbox();
 				_mailbox.Subscribe<ImageReplacementArgs>(DesktopMessages.ReplaceImage, OnReplaceImage);
+				_mailbox.Subscribe(DesktopMessages.ToggleImages, OnToggleImages);
 			}
 
 			_textBorder = new Pen(Color.Black, TextBorder);
@@ -62,6 +66,11 @@ namespace SPNATI_Character_Editor.Controls
 			}
 		}
 
+		private void OnToggleImages()
+		{
+			canvas.Invalidate();
+		}
+
 		private void UpdateFont()
 		{
 			_textFont?.Dispose();
@@ -69,7 +78,7 @@ namespace SPNATI_Character_Editor.Controls
 			int screenWidth = (int)(canvas.Height * 1.33f);
 
 			float size = 14 * (screenWidth / 1000f);
-			_textFont = new Font("Trebuchet MS", size);
+			_textFont = new Font("Trebuchet MS", size == 0 ? 14 : size);
 		}
 
 		private void UpdateSceneTransform()
@@ -77,7 +86,7 @@ namespace SPNATI_Character_Editor.Controls
 			SceneTransform = new Matrix();
 			int screenHeight = canvas.Height - ScreenMargin * 2;
 			int availableHeight = ShowTextBox ? (int)(screenHeight * (1 - TextPercent)) : (int)(screenHeight * 0.9f);
-			float screenScale = availableHeight / (_imagePose == null ? 1400.0f : _imagePose.BaseHeight);
+			float screenScale = availableHeight / (Pose == null ? 1400.0f : Pose.BaseHeight);
 			SceneTransform.Scale(screenScale, screenScale, MatrixOrder.Append); // scale to display
 			SceneTransform.Translate(canvas.Width * 0.5f, screenHeight - availableHeight, MatrixOrder.Append); // center horizontally
 		}
@@ -89,7 +98,7 @@ namespace SPNATI_Character_Editor.Controls
 				_imageReference = null;
 				if (_image.GetPose() != null)
 				{
-					_imagePose = null;
+					Pose = null;
 				}
 				else
 				{
@@ -114,6 +123,11 @@ namespace SPNATI_Character_Editor.Controls
 			canvas.Invalidate();
 		}
 
+		public void SetMarkers(List<string> markers)
+		{
+			_markers = markers;
+		}
+
 		public void SetImage(CharacterImage image)
 		{
 			if (_image == image)
@@ -127,7 +141,7 @@ namespace SPNATI_Character_Editor.Controls
 			}
 			UpdateSceneTransform();
 			_image = image;
-			_imagePose = null;
+			Pose = null;
 			tmrTick.Stop();
 			if (image == null)
 			{
@@ -137,10 +151,13 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				if (image.GetPose() != null)
 				{
-					_imagePose = new LivePose(image.Skin, image.GetPose());
-					_time = 0;
-					_lastTick = DateTime.Now;
-					tmrTick.Enabled = true;
+					Pose = new LivePose(image.Skin, image.GetPose());
+					if (AutoPlayback)
+					{
+						_time = 0;
+						_lastTick = DateTime.Now;
+						tmrTick.Enabled = true;
+					}
 				}
 				else
 				{
@@ -167,7 +184,7 @@ namespace SPNATI_Character_Editor.Controls
 				canvas.Invalidate();
 				if (_image.GetPose() != null)
 				{
-					_imagePose = new LivePose(_image.Skin, _image.GetPose());
+					Pose = new LivePose(_image.Skin, _image.GetPose());
 					_time = 0;
 				}
 				_imageReference = args.NewImage;
@@ -176,6 +193,9 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void canvas_Paint(object sender, PaintEventArgs e)
 		{
+			if (Config.GetBoolean(Settings.HideImages))
+				return;
+
 			Graphics g = e.Graphics;
 
 			//text box
@@ -201,24 +221,31 @@ namespace SPNATI_Character_Editor.Controls
 				bounds.Height = Math.Max(size.Height, bounds.Height);
 
 				const int TopOffset = 4;
-				g.FillRectangle(Brushes.White, TextMargin, topPadding + TopOffset, canvas.Width - TextMargin * 2, textboxHeight - TopOffset);
-				g.DrawString(_text, _textFont, Brushes.Black, bounds, sf);
-				g.DrawRectangle(_textBorder, TextMargin, topPadding + TopOffset, canvas.Width - TextMargin * 2, textboxHeight - TopOffset);
-				Point[] triangle = new Point[] {
-					new Point((int)(canvas.Width * _percent) - ArrowSize, topPadding  + textboxHeight - 1),
-					new Point((int)(canvas.Width * _percent) + ArrowSize, topPadding  + textboxHeight - 1),
-					new Point((int)(canvas.Width * _percent), topPadding + textboxHeight + ArrowSize - 1),
-				};
-				g.FillPolygon(Brushes.White, triangle);
-				g.DrawLine(_textBorder, triangle[0], triangle[2]);
-				g.DrawLine(_textBorder, triangle[1], triangle[2]);
+				using (SolidBrush br = new SolidBrush(SkinManager.Instance.CurrentSkin.FieldBackColor))
+				{
+					g.FillRectangle(br, TextMargin, topPadding + TopOffset, canvas.Width - TextMargin * 2, textboxHeight - TopOffset);
+
+					using (SolidBrush fr = new SolidBrush(SkinManager.Instance.CurrentSkin.Surface.ForeColor))
+					{
+						g.DrawString(_text, _textFont, fr, bounds, sf);
+					}
+					g.DrawRectangle(_textBorder, TextMargin, topPadding + TopOffset, canvas.Width - TextMargin * 2, textboxHeight - TopOffset);
+					Point[] triangle = new Point[] {
+						new Point((int)(canvas.Width * _percent) - ArrowSize, topPadding  + textboxHeight - 1),
+						new Point((int)(canvas.Width * _percent) + ArrowSize, topPadding  + textboxHeight - 1),
+						new Point((int)(canvas.Width * _percent), topPadding + textboxHeight + ArrowSize - 1),
+					};
+					g.FillPolygon(br, triangle);
+					g.DrawLine(_textBorder, triangle[0], triangle[2]);
+					g.DrawLine(_textBorder, triangle[1], triangle[2]);
+				}
 			}
 
-			if (_imagePose != null)
+			if (Pose != null)
 			{
-				foreach (LiveSprite sprite in _imagePose.DrawingOrder)
+				foreach (LiveSprite sprite in Pose.DrawingOrder)
 				{
-					sprite.Draw(g, SceneTransform, new List<string>());
+					sprite.Draw(g, SceneTransform, _markers, true);
 				}
 			}
 			else if (_imageReference != null)
@@ -232,6 +259,25 @@ namespace SPNATI_Character_Editor.Controls
 			}
 		}
 
+		/// <summary>
+		/// Converts what's currently visible into an image
+		/// </summary>
+		/// <returns></returns>
+		public Bitmap GetImage()
+		{
+			Bitmap bmp = new Bitmap(canvas.Width, canvas.Height);
+			canvas.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
+			return bmp;
+		}
+
+		public void SetTime(float time)
+		{
+			_time = time;
+			Pose.UpdateTime(_time, _time, true);
+			canvas.Invalidate();
+			canvas.Update();
+		}
+
 		private void tmrTick_Tick(object sender, EventArgs e)
 		{
 			DateTime now = DateTime.Now;
@@ -240,13 +286,13 @@ namespace SPNATI_Character_Editor.Controls
 			_lastTick = now;
 			_time += elapsedSec;
 
-			if (_imagePose == null)
+			if (Pose == null)
 			{
 				tmrTick.Enabled = false;
 				return;
 			}
 
-			_imagePose.UpdateTime(_time, true);
+			Pose.UpdateTime(_time, _time, true);
 			canvas.Invalidate();
 		}
 
@@ -255,5 +301,10 @@ namespace SPNATI_Character_Editor.Controls
 			UpdateFont();
 			UpdateSceneTransform();
 		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			canvas.BackColor = skin.Background.Normal;
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/DialogueAdvancedControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/DialogueAdvancedControl.Designer.cs
index 1f3b282c51b4db0cfa342e4461769cdcf89f8570..51446befdb1608bdb90cf2640c35e1f7fa993c1e 100644
--- a/editor source/SPNATI Character Editor/Controls/DialogueAdvancedControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/DialogueAdvancedControl.Designer.cs	
@@ -28,22 +28,23 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.groupBox1 = new System.Windows.Forms.GroupBox();
-			this.valLocation = new System.Windows.Forms.NumericUpDown();
-			this.label2 = new System.Windows.Forms.Label();
-			this.cboDirection = new System.Windows.Forms.ComboBox();
-			this.label1 = new System.Windows.Forms.Label();
-			this.groupBox2 = new System.Windows.Forms.GroupBox();
-			this.txtLabel = new System.Windows.Forms.TextBox();
-			this.cboAI = new System.Windows.Forms.ComboBox();
-			this.label7 = new System.Windows.Forms.Label();
-			this.label6 = new System.Windows.Forms.Label();
-			this.cboSize = new System.Windows.Forms.ComboBox();
-			this.label3 = new System.Windows.Forms.Label();
-			this.cboGender = new System.Windows.Forms.ComboBox();
-			this.label4 = new System.Windows.Forms.Label();
-			this.label5 = new System.Windows.Forms.Label();
-			this.valWeight = new System.Windows.Forms.NumericUpDown();
+			this.groupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.valLocation = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.cboDirection = new Desktop.Skinning.SkinnedComboBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.groupBox2 = new Desktop.Skinning.SkinnedGroupBox();
+			this.txtLabel = new Desktop.Skinning.SkinnedTextBox();
+			this.cboAI = new Desktop.Skinning.SkinnedComboBox();
+			this.label7 = new Desktop.Skinning.SkinnedLabel();
+			this.label6 = new Desktop.Skinning.SkinnedLabel();
+			this.cboSize = new Desktop.Skinning.SkinnedComboBox();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.cboGender = new Desktop.Skinning.SkinnedComboBox();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
+			this.valWeight = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.chkOneShot = new Desktop.Skinning.SkinnedCheckBox();
 			this.groupBox1.SuspendLayout();
 			((System.ComponentModel.ISupportInitialize)(this.valLocation)).BeginInit();
 			this.groupBox2.SuspendLayout();
@@ -56,16 +57,29 @@
 			this.groupBox1.Controls.Add(this.label2);
 			this.groupBox1.Controls.Add(this.cboDirection);
 			this.groupBox1.Controls.Add(this.label1);
-			this.groupBox1.Location = new System.Drawing.Point(3, 84);
+			this.groupBox1.Location = new System.Drawing.Point(3, 90);
 			this.groupBox1.Name = "groupBox1";
-			this.groupBox1.Size = new System.Drawing.Size(200, 73);
+			this.groupBox1.Size = new System.Drawing.Size(200, 79);
 			this.groupBox1.TabIndex = 0;
 			this.groupBox1.TabStop = false;
 			this.groupBox1.Text = "Arrow";
 			// 
 			// valLocation
 			// 
-			this.valLocation.Location = new System.Drawing.Point(80, 46);
+			this.valLocation.BackColor = System.Drawing.Color.White;
+			this.valLocation.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valLocation.ForeColor = System.Drawing.Color.Black;
+			this.valLocation.Location = new System.Drawing.Point(80, 51);
+			this.valLocation.Maximum = new decimal(new int[] {
+            100000,
+            0,
+            0,
+            0});
+			this.valLocation.Minimum = new decimal(new int[] {
+            100000,
+            0,
+            0,
+            -2147483648});
 			this.valLocation.Name = "valLocation";
 			this.valLocation.Size = new System.Drawing.Size(114, 20);
 			this.valLocation.TabIndex = 3;
@@ -74,7 +88,11 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(6, 49);
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label2.Location = new System.Drawing.Point(6, 54);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(68, 13);
 			this.label2.TabIndex = 2;
@@ -82,17 +100,29 @@
 			// 
 			// cboDirection
 			// 
+			this.cboDirection.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboDirection.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboDirection.BackColor = System.Drawing.Color.White;
 			this.cboDirection.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboDirection.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboDirection.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboDirection.FormattingEnabled = true;
-			this.cboDirection.Location = new System.Drawing.Point(80, 19);
+			this.cboDirection.Location = new System.Drawing.Point(80, 24);
 			this.cboDirection.Name = "cboDirection";
+			this.cboDirection.SelectedIndex = -1;
+			this.cboDirection.SelectedItem = null;
 			this.cboDirection.Size = new System.Drawing.Size(114, 21);
+			this.cboDirection.Sorted = false;
 			this.cboDirection.TabIndex = 1;
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(6, 22);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label1.Location = new System.Drawing.Point(6, 27);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(52, 13);
 			this.label1.TabIndex = 0;
@@ -110,37 +140,46 @@
 			this.groupBox2.Controls.Add(this.label4);
 			this.groupBox2.Location = new System.Drawing.Point(3, 6);
 			this.groupBox2.Name = "groupBox2";
-			this.groupBox2.Size = new System.Drawing.Size(389, 73);
+			this.groupBox2.Size = new System.Drawing.Size(389, 78);
 			this.groupBox2.TabIndex = 1;
 			this.groupBox2.TabStop = false;
 			this.groupBox2.Text = "Change state";
 			// 
 			// txtLabel
 			// 
-			this.txtLabel.Location = new System.Drawing.Point(270, 46);
+			this.txtLabel.BackColor = System.Drawing.Color.White;
+			this.txtLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtLabel.ForeColor = System.Drawing.Color.Black;
+			this.txtLabel.Location = new System.Drawing.Point(270, 51);
 			this.txtLabel.Name = "txtLabel";
 			this.txtLabel.Size = new System.Drawing.Size(113, 20);
 			this.txtLabel.TabIndex = 7;
 			// 
 			// cboAI
 			// 
+			this.cboAI.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboAI.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboAI.BackColor = System.Drawing.Color.White;
 			this.cboAI.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboAI.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboAI.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboAI.FormattingEnabled = true;
-			this.cboAI.Items.AddRange(new object[] {
-            "",
-            "bad",
-            "average",
-            "good",
-            "best"});
-			this.cboAI.Location = new System.Drawing.Point(270, 19);
+			this.cboAI.Location = new System.Drawing.Point(270, 24);
 			this.cboAI.Name = "cboAI";
+			this.cboAI.SelectedIndex = -1;
+			this.cboAI.SelectedItem = null;
 			this.cboAI.Size = new System.Drawing.Size(113, 21);
+			this.cboAI.Sorted = false;
 			this.cboAI.TabIndex = 6;
 			// 
 			// label7
 			// 
 			this.label7.AutoSize = true;
-			this.label7.Location = new System.Drawing.Point(200, 49);
+			this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label7.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label7.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label7.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label7.Location = new System.Drawing.Point(200, 54);
 			this.label7.Name = "label7";
 			this.label7.Size = new System.Drawing.Size(36, 13);
 			this.label7.TabIndex = 5;
@@ -149,7 +188,11 @@
 			// label6
 			// 
 			this.label6.AutoSize = true;
-			this.label6.Location = new System.Drawing.Point(200, 22);
+			this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label6.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label6.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label6.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label6.Location = new System.Drawing.Point(200, 27);
 			this.label6.Name = "label6";
 			this.label6.Size = new System.Drawing.Size(64, 13);
 			this.label6.TabIndex = 4;
@@ -157,22 +200,29 @@
 			// 
 			// cboSize
 			// 
+			this.cboSize.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboSize.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboSize.BackColor = System.Drawing.Color.White;
 			this.cboSize.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboSize.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboSize.FormattingEnabled = true;
-			this.cboSize.Items.AddRange(new object[] {
-            "",
-            "small",
-            "medium",
-            "large"});
-			this.cboSize.Location = new System.Drawing.Point(80, 46);
+			this.cboSize.Location = new System.Drawing.Point(80, 51);
 			this.cboSize.Name = "cboSize";
+			this.cboSize.SelectedIndex = -1;
+			this.cboSize.SelectedItem = null;
 			this.cboSize.Size = new System.Drawing.Size(114, 21);
+			this.cboSize.Sorted = false;
 			this.cboSize.TabIndex = 3;
 			// 
 			// label3
 			// 
 			this.label3.AutoSize = true;
-			this.label3.Location = new System.Drawing.Point(6, 49);
+			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label3.Location = new System.Drawing.Point(6, 54);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(30, 13);
 			this.label3.TabIndex = 2;
@@ -180,21 +230,29 @@
 			// 
 			// cboGender
 			// 
+			this.cboGender.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboGender.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboGender.BackColor = System.Drawing.Color.White;
 			this.cboGender.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboGender.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboGender.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboGender.FormattingEnabled = true;
-			this.cboGender.Items.AddRange(new object[] {
-            "",
-            "female",
-            "male"});
-			this.cboGender.Location = new System.Drawing.Point(80, 19);
+			this.cboGender.Location = new System.Drawing.Point(80, 24);
 			this.cboGender.Name = "cboGender";
+			this.cboGender.SelectedIndex = -1;
+			this.cboGender.SelectedItem = null;
 			this.cboGender.Size = new System.Drawing.Size(114, 21);
+			this.cboGender.Sorted = false;
 			this.cboGender.TabIndex = 1;
 			// 
 			// label4
 			// 
 			this.label4.AutoSize = true;
-			this.label4.Location = new System.Drawing.Point(6, 22);
+			this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label4.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label4.Location = new System.Drawing.Point(6, 27);
 			this.label4.Name = "label4";
 			this.label4.Size = new System.Drawing.Size(45, 13);
 			this.label4.TabIndex = 0;
@@ -203,7 +261,11 @@
 			// label5
 			// 
 			this.label5.AutoSize = true;
-			this.label5.Location = new System.Drawing.Point(3, 160);
+			this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label5.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label5.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label5.Location = new System.Drawing.Point(5, 176);
 			this.label5.Name = "label5";
 			this.label5.Size = new System.Drawing.Size(44, 13);
 			this.label5.TabIndex = 2;
@@ -211,8 +273,11 @@
 			// 
 			// valWeight
 			// 
+			this.valWeight.BackColor = System.Drawing.Color.White;
 			this.valWeight.DecimalPlaces = 2;
-			this.valWeight.Location = new System.Drawing.Point(53, 158);
+			this.valWeight.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valWeight.ForeColor = System.Drawing.Color.Black;
+			this.valWeight.Location = new System.Drawing.Point(55, 174);
 			this.valWeight.Minimum = new decimal(new int[] {
             1,
             0,
@@ -226,17 +291,28 @@
             0,
             0,
             196608});
+			// 
+			// chkOneShot
+			// 
+			this.chkOneShot.AutoSize = true;
+			this.chkOneShot.Location = new System.Drawing.Point(121, 175);
+			this.chkOneShot.Name = "chkOneShot";
+			this.chkOneShot.Size = new System.Drawing.Size(75, 17);
+			this.chkOneShot.TabIndex = 5;
+			this.chkOneShot.Text = "Play Once";
+			this.chkOneShot.UseVisualStyleBackColor = true;
 			// 
 			// DialogueAdvancedControl
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.chkOneShot);
 			this.Controls.Add(this.valWeight);
 			this.Controls.Add(this.label5);
 			this.Controls.Add(this.groupBox2);
 			this.Controls.Add(this.groupBox1);
 			this.Name = "DialogueAdvancedControl";
-			this.Size = new System.Drawing.Size(395, 183);
+			this.Size = new System.Drawing.Size(395, 195);
 			this.groupBox1.ResumeLayout(false);
 			this.groupBox1.PerformLayout();
 			((System.ComponentModel.ISupportInitialize)(this.valLocation)).EndInit();
@@ -250,21 +326,22 @@
 
 		#endregion
 
-		private System.Windows.Forms.GroupBox groupBox1;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.ComboBox cboDirection;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.NumericUpDown valLocation;
-		private System.Windows.Forms.GroupBox groupBox2;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.ComboBox cboGender;
-		private System.Windows.Forms.Label label4;
-		private System.Windows.Forms.Label label5;
-		private System.Windows.Forms.NumericUpDown valWeight;
-		private System.Windows.Forms.Label label6;
-		private System.Windows.Forms.ComboBox cboSize;
-		private System.Windows.Forms.TextBox txtLabel;
-		private System.Windows.Forms.ComboBox cboAI;
-		private System.Windows.Forms.Label label7;
+		private Desktop.Skinning.SkinnedGroupBox groupBox1;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedComboBox cboDirection;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedNumericUpDown valLocation;
+		private Desktop.Skinning.SkinnedGroupBox groupBox2;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedComboBox cboGender;
+		private Desktop.Skinning.SkinnedLabel label4;
+		private Desktop.Skinning.SkinnedLabel label5;
+		private Desktop.Skinning.SkinnedNumericUpDown valWeight;
+		private Desktop.Skinning.SkinnedLabel label6;
+		private Desktop.Skinning.SkinnedComboBox cboSize;
+		private Desktop.Skinning.SkinnedTextBox txtLabel;
+		private Desktop.Skinning.SkinnedComboBox cboAI;
+		private Desktop.Skinning.SkinnedLabel label7;
+		private Desktop.Skinning.SkinnedCheckBox chkOneShot;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/DialogueAdvancedControl.cs b/editor source/SPNATI Character Editor/Controls/DialogueAdvancedControl.cs
index 872508331477582ca7f5ad58c599bd0e6f9fc63a..8178a5a1693f53ade182206c5f9e53920a85de1b 100644
--- a/editor source/SPNATI Character Editor/Controls/DialogueAdvancedControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/DialogueAdvancedControl.cs	
@@ -1,4 +1,5 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Globalization;
 using System.Windows.Forms;
 
@@ -7,6 +8,13 @@ namespace SPNATI_Character_Editor.Controls
 	public partial class DialogueAdvancedControl : UserControl, IDialogueDropDownControl
 	{
 		public int RowIndex { get; private set; }
+
+		public SkinnedBackgroundType PanelType
+		{
+			get { return SkinnedBackgroundType.Background; }
+		}
+
+		private Character _character;
 		private DialogueLine _line;
 
 		public event EventHandler DataUpdated;
@@ -14,19 +22,36 @@ namespace SPNATI_Character_Editor.Controls
 		public DialogueAdvancedControl()
 		{
 			InitializeComponent();
+			cboGender.Items.AddRange(new string[] { "", "female", "male" });
+			cboSize.Items.AddRange(new string[] { "", "small", "medium", "large" });
 			cboDirection.DataSource = DialogueLine.ArrowDirections;
+			cboAI.DataSource = DialogueLine.AILevels;
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
 		}
 
-		public void SetData(int row, DialogueLine line)
+		public void OnUpdateSkin(Skin skin)
 		{
+			BackColor = skin.Background.Normal;
+			foreach (Control child in Controls)
+			{
+				SkinManager.Instance.ReskinControl(child, skin);
+			}
+			Invalidate(true);
+		}
+
+		public void SetData(int row, DialogueLine line, Character character)
+		{
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
 			RowIndex = row;
 			_line = line;
+			_character = character;
 			cboDirection.Text = line.Direction ?? "";
 			
 			cboSize.Text = line.Size ?? "";
 			cboAI.Text = line.Intelligence ?? "";
 			cboGender.Text = line.Gender ?? "";
 			txtLabel.Text = line.Label;
+			chkOneShot.Checked = line.OneShotId > 0;
 
 			valWeight.Value = Math.Max(valWeight.Minimum, Math.Min(valWeight.Maximum, (decimal)line.Weight));
 
@@ -91,6 +116,18 @@ namespace SPNATI_Character_Editor.Controls
 			}
 			_line.Label = label;
 
+			if (chkOneShot.Checked)
+			{
+				if (_line.OneShotId == 0)
+				{
+					_line.OneShotId = ++_character.Behavior.MaxStageId;
+				}
+			}
+			else
+			{
+				_line.OneShotId = 0;
+			}
+
 			return _line;
 		}
 
@@ -100,11 +137,11 @@ namespace SPNATI_Character_Editor.Controls
 		}
 	}
 
-	public interface IDialogueDropDownControl
+	public interface IDialogueDropDownControl : ISkinnedPanel, ISkinControl
 	{
 		event EventHandler DataUpdated;
 		int RowIndex { get; }
-		void SetData(int rowIndex, DialogueLine line);
+		void SetData(int rowIndex, DialogueLine line, Character character);
 		DialogueLine GetLine();
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/DialogueGrid.Designer.cs b/editor source/SPNATI Character Editor/Controls/DialogueGrid.Designer.cs
index 2dc7393460e1f05b06a48ff75d12788a0a853f39..bb3431ab8a639e1e09a6863f2fa543187db0741e 100644
--- a/editor source/SPNATI Character Editor/Controls/DialogueGrid.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/DialogueGrid.Designer.cs	
@@ -28,51 +28,72 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
-			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
-			this.gridDialogue = new SPNATI_Character_Editor.KeyboardDataGridView();
-			this.ColImage = new System.Windows.Forms.DataGridViewComboBoxColumn();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle8 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle9 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.gridDialogue = new Desktop.Skinning.SkinnedDataGridView();
+			this.ColImage = new Desktop.Skinning.SkinnedDataGridViewComboBoxColumn();
+			this.ColImageOptions = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
 			this.ColText = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColMarker = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColMarkerOptions = new System.Windows.Forms.DataGridViewButtonColumn();
-			this.ColTrophy = new System.Windows.Forms.DataGridViewButtonColumn();
-			this.ColMore = new System.Windows.Forms.DataGridViewButtonColumn();
-			this.ColDelete = new System.Windows.Forms.DataGridViewButtonColumn();
+			this.ColMarkerOptions = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
+			this.ColTrophy = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
+			this.ColMore = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
+			this.ColDelete = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
 			((System.ComponentModel.ISupportInitialize)(this.gridDialogue)).BeginInit();
 			this.SuspendLayout();
 			// 
 			// gridDialogue
 			// 
-			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
-			dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control;
-			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText;
-			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
-			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
-			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
-			this.gridDialogue.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
+			this.gridDialogue.BackgroundColor = System.Drawing.Color.White;
+			this.gridDialogue.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridDialogue.ClipboardCopyMode = System.Windows.Forms.DataGridViewClipboardCopyMode.EnableWithoutHeaderText;
+			this.gridDialogue.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle7.BackColor = System.Drawing.SystemColors.Control;
+			dataGridViewCellStyle7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			dataGridViewCellStyle7.ForeColor = System.Drawing.SystemColors.WindowText;
+			dataGridViewCellStyle7.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle7.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle7.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridDialogue.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle7;
 			this.gridDialogue.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridDialogue.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColImage,
+            this.ColImageOptions,
             this.ColText,
             this.ColMarker,
             this.ColMarkerOptions,
             this.ColTrophy,
             this.ColMore,
             this.ColDelete});
-			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
-			dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window;
-			dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
-			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
-			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
-			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
-			this.gridDialogue.DefaultCellStyle = dataGridViewCellStyle2;
+			dataGridViewCellStyle8.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle8.BackColor = System.Drawing.SystemColors.Window;
+			dataGridViewCellStyle8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			dataGridViewCellStyle8.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle8.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle8.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle8.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridDialogue.DefaultCellStyle = dataGridViewCellStyle8;
 			this.gridDialogue.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.gridDialogue.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
+			this.gridDialogue.EnableHeadersVisualStyles = false;
+			this.gridDialogue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridDialogue.GridColor = System.Drawing.Color.LightGray;
 			this.gridDialogue.Location = new System.Drawing.Point(0, 0);
 			this.gridDialogue.MultiSelect = false;
 			this.gridDialogue.Name = "gridDialogue";
+			this.gridDialogue.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle9.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle9.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle9.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle9.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle9.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle9.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridDialogue.RowHeadersDefaultCellStyle = dataGridViewCellStyle9;
+			this.gridDialogue.RowHeadersVisible = false;
+			this.gridDialogue.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
 			this.gridDialogue.Size = new System.Drawing.Size(572, 380);
 			this.gridDialogue.TabIndex = 42;
 			this.gridDialogue.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridDialogue_CellContentClick);
@@ -88,11 +109,21 @@
 			// 
 			// ColImage
 			// 
+			this.ColImage.AutoComplete = false;
+			this.ColImage.DisplayMember = null;
 			this.ColImage.HeaderText = "Image";
-			this.ColImage.MaxDropDownItems = 20;
 			this.ColImage.Name = "ColImage";
+			this.ColImage.Sorted = false;
 			this.ColImage.Width = 120;
 			// 
+			// ColImageOptions
+			// 
+			this.ColImageOptions.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColImageOptions.Flat = false;
+			this.ColImageOptions.HeaderText = "";
+			this.ColImageOptions.Name = "ColImageOptions";
+			this.ColImageOptions.Width = 21;
+			// 
 			// ColText
 			// 
 			this.ColText.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
@@ -108,24 +139,32 @@
 			// 
 			// ColMarkerOptions
 			// 
+			this.ColMarkerOptions.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColMarkerOptions.Flat = false;
 			this.ColMarkerOptions.HeaderText = "";
 			this.ColMarkerOptions.Name = "ColMarkerOptions";
 			this.ColMarkerOptions.Width = 21;
 			// 
 			// ColTrophy
 			// 
+			this.ColTrophy.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColTrophy.Flat = false;
 			this.ColTrophy.HeaderText = "";
 			this.ColTrophy.Name = "ColTrophy";
 			this.ColTrophy.Width = 21;
 			// 
 			// ColMore
 			// 
+			this.ColMore.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColMore.Flat = false;
 			this.ColMore.HeaderText = "";
 			this.ColMore.Name = "ColMore";
 			this.ColMore.Width = 21;
 			// 
 			// ColDelete
 			// 
+			this.ColDelete.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.ColDelete.Flat = false;
 			this.ColDelete.HeaderText = "";
 			this.ColDelete.Name = "ColDelete";
 			this.ColDelete.Width = 21;
@@ -145,13 +184,14 @@
 
 		#endregion
 
-		private KeyboardDataGridView gridDialogue;
-		private System.Windows.Forms.DataGridViewComboBoxColumn ColImage;
+		private Desktop.Skinning.SkinnedDataGridView gridDialogue;
+		private Desktop.Skinning.SkinnedDataGridViewComboBoxColumn ColImage;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColImageOptions;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColText;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColMarker;
-		private System.Windows.Forms.DataGridViewButtonColumn ColMarkerOptions;
-		private System.Windows.Forms.DataGridViewButtonColumn ColTrophy;
-		private System.Windows.Forms.DataGridViewButtonColumn ColMore;
-		private System.Windows.Forms.DataGridViewButtonColumn ColDelete;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColMarkerOptions;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColTrophy;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColMore;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColDelete;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/DialogueGrid.cs b/editor source/SPNATI Character Editor/Controls/DialogueGrid.cs
index 002bd958920e3619474cb09e9899db146a58331f..10d61ccc2a9789953d246583d49b48f1ae883ae4 100644
--- a/editor source/SPNATI Character Editor/Controls/DialogueGrid.cs	
+++ b/editor source/SPNATI Character Editor/Controls/DialogueGrid.cs	
@@ -1,14 +1,14 @@
-using SPNATI_Character_Editor.Forms;
+using Desktop.Skinning;
+using SPNATI_Character_Editor.Forms;
 using System;
 using System.Collections.Generic;
 using System.Drawing;
-using System.Globalization;
 using System.Text.RegularExpressions;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Controls
 {
-	public partial class DialogueGrid : UserControl
+	public partial class DialogueGrid : UserControl, ISkinControl
 	{
 		private Case _selectedCase;
 		private Stage _selectedStage;
@@ -20,6 +20,7 @@ namespace SPNATI_Character_Editor.Controls
 		private ToolStripDropDown _markerDropDown;
 		private IDialogueDropDownControl _lineCtl;
 		private ToolStripDropDown _lineDropDown;
+		private ToolStripDropDown _activeDropdown;
 
 		#region Events
 		public new event EventHandler<KeyEventArgs> KeyDown;
@@ -40,19 +41,13 @@ namespace SPNATI_Character_Editor.Controls
 				if (_readOnly)
 				{
 					gridDialogue.AllowUserToAddRows = false;
-					gridDialogue.AllowUserToDeleteRows = false;
-					gridDialogue.RowHeadersVisible = false;
 					gridDialogue.EditMode = DataGridViewEditMode.EditProgrammatically;
-					gridDialogue.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
 					ColDelete.Visible = false;
 				}
 				else
 				{
 					gridDialogue.AllowUserToAddRows = true;
-					gridDialogue.AllowUserToDeleteRows = true;
-					gridDialogue.RowHeadersVisible = true;
 					gridDialogue.EditMode = DataGridViewEditMode.EditOnEnter;
-					gridDialogue.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect;
 					ColDelete.Visible = true;
 				}
 			}
@@ -62,6 +57,8 @@ namespace SPNATI_Character_Editor.Controls
 		{
 			InitializeComponent();
 
+			ColTrophy.Flat = ColDelete.Flat = ColMore.Flat = ColMarkerOptions.Flat = ColImageOptions.Flat = true;
+
 			_markerCtl = new MarkerOptions();
 			CreateDropdown(_markerCtl, out _markerDropDown);
 
@@ -75,6 +72,29 @@ namespace SPNATI_Character_Editor.Controls
 			Controls.Add(_intellisense);
 		}
 
+		protected override void OnCreateControl()
+		{
+			base.OnCreateControl();
+			foreach (Control ctl in Controls)
+			{
+				ctl.MouseDown += Ctl_MouseDown;
+			}
+		}
+
+		protected override void OnVisibleChanged(EventArgs e)
+		{
+			if (!Visible)
+			{
+				HideDropdown();
+			}
+			base.OnVisibleChanged(e);
+		}
+
+		private void Ctl_MouseDown(object sender, MouseEventArgs e)
+		{
+			HideDropdown();
+		}
+
 		private void CreateDropdown(IDialogueDropDownControl ctl, out ToolStripDropDown dropdown)
 		{
 			Control hostCtl = ctl as Control;
@@ -84,10 +104,17 @@ namespace SPNATI_Character_Editor.Controls
 			host.Margin = new Padding(0);
 			dropdown.Padding = new Padding(0);
 			dropdown.Items.Add(host);
-			dropdown.Closing += _markerDropDown_Closing;
+			dropdown.AutoClose = false;
+			dropdown.Closing += DropDownClosing;
 			ctl.DataUpdated += Ctl_DataUpdated;
 		}
 
+		public void SetStage(Stage stage, HashSet<int> stages)
+		{
+			_selectedStage = stage;
+			UpdateAvailableImagesForCase(stages, true);
+		}
+
 		public void SetData(Character character, Case c)
 		{
 			Stage stage = null;
@@ -100,11 +127,17 @@ namespace SPNATI_Character_Editor.Controls
 				}
 				stages.Add(s);
 			}
-			SetData(character, stage, c, stages, ImageLibrary.Get(character));
+			ImageLibrary library = null;
+			if (character != null)
+			{
+				library = ImageLibrary.Get(character);
+			}
+			SetData(character, stage, c, stages, library);
 		}
 
 		public void SetData(Character character, Stage stage, Case c, HashSet<int> selectedStages, ImageLibrary imageLibrary)
 		{
+			HideDropdown();
 			_character = character;
 			_selectedStage = stage;
 			_selectedCase = c;
@@ -120,7 +153,7 @@ namespace SPNATI_Character_Editor.Controls
 
 			//Populate lines
 			gridDialogue.Rows.Clear();
-			List<DialogueLine> lines = (_selectedCase.Tag == Trigger.StartTrigger ? _character.StartingLines : _selectedCase.Lines);
+			List<DialogueLine> lines = (_selectedCase.Tag == Trigger.StartTrigger && _character != null ? _character.StartingLines : _selectedCase.Lines);
 			foreach (DialogueLine line in lines)
 			{
 				AddLineToDialogueGrid(line, null);
@@ -132,10 +165,14 @@ namespace SPNATI_Character_Editor.Controls
 
 		public void Save()
 		{
-			List<DialogueLine> lines = (_selectedCase.Tag == Trigger.StartTrigger ? _character.StartingLines : _selectedCase.Lines);
-			foreach (DialogueLine line in lines)
+			HideDropdown();
+			List<DialogueLine> lines = (_selectedCase.Tag == Trigger.StartTrigger && _character != null ? _character.StartingLines : _selectedCase.Lines);
+			if (_character != null)
 			{
-				_character.RemoveMarkerReference(line.Marker);
+				foreach (DialogueLine line in lines)
+				{
+					_character.RemoveMarkerReference(line.Marker);
+				}
 			}
 			lines.Clear();
 			for (int i = 0; i < gridDialogue.Rows.Count; i++)
@@ -144,7 +181,7 @@ namespace SPNATI_Character_Editor.Controls
 				if (line != null)
 				{
 					lines.Add(line);
-					_character.CacheMarker(line.Marker);
+					_character?.CacheMarker(line.Marker);
 				}
 			}
 		}
@@ -171,10 +208,14 @@ namespace SPNATI_Character_Editor.Controls
 			bool perTarget;
 			string marker = Marker.ExtractPieces(taggedLine.Marker, out markerValue, out perTarget);
 			marker = row.Cells["ColMarker"].Value?.ToString();
-			if (string.IsNullOrEmpty(marker))
+			if (string.IsNullOrWhiteSpace(marker))
 			{
 				marker = null;
 			}
+			else
+			{
+				marker = marker.Trim();
+			}
 
 			if (text == "~silent~")
 			{
@@ -187,7 +228,7 @@ namespace SPNATI_Character_Editor.Controls
 				image = DialogueLine.GetDefaultImage(image);
 			}
 			DialogueLine line = new DialogueLine(image, text);
-
+			line.StageImages = taggedLine.StageImages;
 			line.IsMarkerPersistent = taggedLine.IsMarkerPersistent;
 			line.Direction = taggedLine.Direction;
 			line.Location = taggedLine.Location;
@@ -196,6 +237,7 @@ namespace SPNATI_Character_Editor.Controls
 			line.Intelligence = taggedLine.Intelligence;
 			line.Label = taggedLine.Label;
 			line.Gender = taggedLine.Gender;
+			line.OneShotId = taggedLine.OneShotId;
 
 			if (perTarget)
 			{
@@ -256,78 +298,96 @@ namespace SPNATI_Character_Editor.Controls
 			}
 
 			int stageId = _selectedStage == null ? 0 : _selectedStage.Id;
-			DataGridViewComboBoxColumn col = gridDialogue.Columns["ColImage"] as DataGridViewComboBoxColumn;
+			SkinnedDataGridViewComboBoxColumn col = gridDialogue.Columns["ColImage"] as SkinnedDataGridViewComboBoxColumn;
 			col.Items.Clear();
 			List<CharacterImage> images = new List<CharacterImage>();
-			if (_selectedStage == null)
+			if (_character != null)
 			{
-				images.AddRange(_imageLibrary.GetImages(0));
-				if (Config.UsePrefixlessImages)
+				if (_selectedStage == null)
 				{
-					string prefix = Config.PrefixFilter;
-					foreach (CharacterImage img in _imageLibrary.GetImages(-1))
+					images.AddRange(_imageLibrary.GetImages(0));
+					if (Config.UsePrefixlessImages)
 					{
-						string file = img.Name;
-						if (string.IsNullOrEmpty(prefix) || !file.StartsWith(prefix))
+						foreach (CharacterImage img in _imageLibrary.GetImages(-1))
 						{
-							images.Add(img);
+							string file = img.Name;
+							if (!_imageLibrary.FilterImage(_character, file))
+							{
+								images.Add(img);
+							}
 						}
 					}
+					foreach (var image in images)
+					{
+						col.Items.Add(image);
+					}
 				}
-				foreach (var image in images)
-				{
-					col.Items.Add(image);
-				}
-			}
-			else
-			{
-				images.AddRange(_imageLibrary.GetImages(stageId));
-				if (Config.UsePrefixlessImages)
+				else
 				{
-					string prefix = Config.PrefixFilter;
-					foreach (CharacterImage img in _imageLibrary.GetImages(-1))
+					images.AddRange(_imageLibrary.GetImages(stageId));
+					if (Config.UsePrefixlessImages)
 					{
-						string file = img.Name;
-						if (string.IsNullOrEmpty(prefix) || !file.StartsWith(prefix))
+						foreach (CharacterImage img in _imageLibrary.GetImages(-1))
 						{
-							images.Add(img);
+							string file = img.Name;
+							if (!_imageLibrary.FilterImage(_character, file))
+							{
+								images.Add(img);
+							}
 						}
 					}
-				}
 
-				foreach (var image in images)
-				{
-					string name = DialogueLine.GetDefaultImage(image.Name);
-					//Filter out the ones that don't appear in every selected stage
-					bool allExist = true;
-					if (!image.IsGeneric)
+					foreach (var image in images)
 					{
-						bool custom = name.StartsWith("custom:");
-						string nameWithoutStage = name;
-						if (custom)
+						string name = DialogueLine.GetDefaultImage(image.Name);
+						//Filter out the ones that don't appear in every selected stage, unless there are stage-specific images, which would result in a blank field
+						bool stageSpecific = _selectedCase.Lines.Find(l => l.StageImages.Count > 0) != null;
+						bool allExist = true;
+						if (!image.IsGeneric && !stageSpecific)
 						{
-							nameWithoutStage = DialogueLine.GetDefaultImage(image.Name.Substring(7));
-						}
-						foreach (int stage in selectedStages)
-						{
-							string key = stage + "-" + nameWithoutStage;
+							bool custom = name.StartsWith("custom:");
+							string nameWithoutStage = name;
 							if (custom)
 							{
-								key = "custom:" + key;
+								nameWithoutStage = DialogueLine.GetDefaultImage(image.Name.Substring(7));
 							}
-							if (_imageLibrary.Find(key) == null)
+							foreach (int stage in selectedStages)
 							{
-								allExist = false;
-								break;
+								string key = stage + "-" + nameWithoutStage;
+								if (custom)
+								{
+									key = "custom:" + key;
+								}
+								if (_imageLibrary.Find(key) == null)
+								{
+									allExist = false;
+									break;
+								}
 							}
-						}
 
+						}
+						if (allExist)
+						{
+							col.Items.Add(image);
+						}
 					}
-					if (allExist)
-						col.Items.Add(image);
 				}
 			}
 
+			foreach (DataGridViewRow row in gridDialogue.Rows)
+			{
+				SkinnedDataGridViewComboBoxCell cellCol = row.Cells[ColImage.Index] as SkinnedDataGridViewComboBoxCell;
+				if (cellCol != null)
+				{
+					object old = cellCol.Value;
+					cellCol.Items.Clear();
+					foreach (object item in col.Items)
+					{
+						cellCol.Items.Add(item);
+					}
+					cellCol.Value = old;
+				}
+			}
 			col.DisplayMember = "DefaultName";
 
 			if (retainValue)
@@ -378,7 +438,8 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void gridDialogue_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
 		{
-			if (e.ColumnIndex == ColDelete.Index || e.ColumnIndex == ColTrophy.Index || e.ColumnIndex == ColMarkerOptions.Index || e.ColumnIndex == ColMore.Index)
+			if (e.ColumnIndex == ColDelete.Index || e.ColumnIndex == ColTrophy.Index || e.ColumnIndex == ColMarkerOptions.Index || e.ColumnIndex == ColMore.Index
+				|| e.ColumnIndex == ColImageOptions.Index)
 			{
 				Image img = Properties.Resources.Delete;
 				if (e.ColumnIndex == ColTrophy.Index)
@@ -412,6 +473,10 @@ namespace SPNATI_Character_Editor.Controls
 				{
 					img = Properties.Resources.Ellipsis;
 				}
+				else if (e.ColumnIndex == ColImageOptions.Index)
+				{
+					img = Properties.Resources.ChevronDown;
+				}
 				e.Paint(e.CellBounds, DataGridViewPaintParts.All);
 				var w = img.Width;
 				var h = img.Height;
@@ -431,7 +496,7 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				if (e.Value != null)
 				{
-					DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell)gridDialogue.Rows[e.RowIndex].Cells[e.ColumnIndex];
+					SkinnedDataGridViewComboBoxCell cell = (SkinnedDataGridViewComboBoxCell)gridDialogue.Rows[e.RowIndex].Cells[e.ColumnIndex];
 					foreach (object item in cell.Items)
 					{
 						if (((CharacterImage)item).DefaultName == e.Value.ToString())
@@ -468,12 +533,12 @@ namespace SPNATI_Character_Editor.Controls
 			if (_selectedCase == null || e.FormattedValue == null || _populatingCase || !Config.UseIntellisense)
 				return;
 
-			List<string> invalidVars = DialogueLine.GetInvalidVariables(_selectedCase.Tag, e.FormattedValue.ToString());
-			if (invalidVars.Count > 0)
-			{
-				MessageBox.Show(string.Format("The following variables are invalid for this line: {0}", string.Join(",", invalidVars)));
-				e.Cancel = true;
-			}
+			//List<string> invalidVars = DialogueLine.GetInvalidVariables(_selectedCase, e.FormattedValue.ToString());
+			//if (invalidVars.Count > 0)
+			//{
+			//	MessageBox.Show(string.Format("The following variables are invalid for this line: {0}", string.Join(",", invalidVars)));
+			//	e.Cancel = true;
+			//}
 		}
 
 		private void gridDialogue_CellValueChanged(object sender, DataGridViewCellEventArgs e)
@@ -504,6 +569,7 @@ namespace SPNATI_Character_Editor.Controls
 			row.Cells["ColTrophy"].ToolTipText = "Unlock collectible";
 			row.Cells[nameof(ColMore)].ToolTipText = "More options";
 			row.Cells[nameof(ColMarkerOptions)].ToolTipText = "Advanced marker options";
+			row.Cells[nameof(ColImageOptions)].ToolTipText = "Stage-specific images";
 		}
 
 		private void gridDialogue_CellContentClick(object sender, DataGridViewCellEventArgs e)
@@ -529,21 +595,41 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				ShowDropdown(e.RowIndex, e.ColumnIndex, _lineCtl, _lineDropDown);
 			}
+			else if (col == ColImageOptions)
+			{
+				if (_character != null)
+				{
+					DialogueLine line = gridDialogue.Rows[e.RowIndex].Tag as DialogueLine;
+					StageImageSelection form = new StageImageSelection(_character, line, _selectedCase);
+					form.ShowDialog();
+				}
+			}
 		}
 
 		private void ShowDropdown(int rowIndex, int colIndex, IDialogueDropDownControl ctl, ToolStripDropDown dropdown)
 		{
 			DataGridViewRow row = gridDialogue.Rows[rowIndex];
 			DialogueLine line = ReadLineFromDialogueGrid(rowIndex);
-			ctl.SetData(rowIndex, line);
+			if (line == null) { return; }
+			ctl.SetData(rowIndex, line, _character);
 
 			Point pt = gridDialogue.PointToScreen(gridDialogue.GetCellDisplayRectangle(colIndex, rowIndex, false).Location);
 			Point screen = gridDialogue.PointToScreen(new Point(0, 0));
 			dropdown.Show(this, new Point(pt.X - screen.X + ColMore.Width, pt.Y - screen.Y + row.Height));
+			_activeDropdown = dropdown;
+		}
+
+		private void HideDropdown()
+		{
+			if (_activeDropdown != null)
+			{
+				_activeDropdown.Close();
+			}
 		}
-		
-		private void _markerDropDown_Closing(object sender, ToolStripDropDownClosingEventArgs e)
+
+		private void DropDownClosing(object sender, ToolStripDropDownClosingEventArgs e)
 		{
+			_activeDropdown = null;
 			Control senderCtl = sender as Control;
 			IDialogueDropDownControl ctl = senderCtl.Tag as IDialogueDropDownControl;
 			DataGridViewRow row = gridDialogue.Rows[ctl.RowIndex];
@@ -640,7 +726,7 @@ namespace SPNATI_Character_Editor.Controls
 				row = gridDialogue.Rows[gridDialogue.Rows.Add()];
 			}
 			row.Tag = line;
-			DataGridViewComboBoxCell imageCell = row.Cells["ColImage"] as DataGridViewComboBoxCell;
+			SkinnedDataGridViewComboBoxCell imageCell = row.Cells["ColImage"] as SkinnedDataGridViewComboBoxCell;
 			SetImage(imageCell, imageKey);
 			DataGridViewCell textCell = row.Cells["ColText"];
 			textCell.Value = line.Text;
@@ -652,7 +738,19 @@ namespace SPNATI_Character_Editor.Controls
 			markerCell.Value = marker;
 			
 			row.Cells[nameof(ColTrophy)].Tag = new Tuple<string, string>(line.CollectibleId, line.CollectibleValue);
+			row.Cells[nameof(ColMarkerOptions)].ToolTipText = GetMarkerTooltip(line);
+		}
 
+		private string GetMarkerTooltip(DialogueLine line)
+		{
+			if (!line.HasAdvancedMarker)
+			{
+				return "Advanced marker options";
+			}
+			else
+			{
+				return line.Marker;
+			}
 		}
 
 		/// <summary>
@@ -660,7 +758,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		/// <param name="cell"></param>
 		/// <param name="key"></param>
-		private void SetImage(DataGridViewComboBoxCell cell, string key)
+		private void SetImage(SkinnedDataGridViewComboBoxCell cell, string key)
 		{
 			string defaultKey = DialogueLine.GetDefaultImage(key);
 			foreach (var item in cell.Items)
@@ -867,6 +965,7 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void ShowTrophyForm(int rowIndex)
 		{
+			if (_character == null) { return; }
 			if (_character.Collectibles.Count == 0)
 			{
 				MessageBox.Show("You haven't created any collectibles yet. Go to the Collectibles tab to create a collectible before attaching it to a dialogue line.");
@@ -887,5 +986,12 @@ namespace SPNATI_Character_Editor.Controls
 				cell.Tag = data;
 			}
 		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			BackColor = skin.GetBackColor(SkinnedBackgroundType.Surface);
+			_markerCtl.OnUpdateSkin(skin);
+			_lineCtl.OnUpdateSkin(skin);
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/DialogueTree.Designer.cs b/editor source/SPNATI Character Editor/Controls/DialogueTree.Designer.cs
index f536bb11532b505b37f96c6c7b19f65a78871383..d34fe90aa5ba66137eca355d77816976a0254063 100644
--- a/editor source/SPNATI Character Editor/Controls/DialogueTree.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/DialogueTree.Designer.cs	
@@ -19,6 +19,10 @@
 			this.tstrDialogue = new System.Windows.Forms.ToolStrip();
 			this.tsbtnAddDialogue = new System.Windows.Forms.ToolStripSplitButton();
 			this.triggerMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
+			this.tsRecipe = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
+			this.tsRefresh = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
 			this.tsbtnSplit = new System.Windows.Forms.ToolStripDropDownButton();
 			this.tsbtnRemoveDialogue = new System.Windows.Forms.ToolStripButton();
 			this.tsConfig = new System.Windows.Forms.ToolStripDropDownButton();
@@ -26,46 +30,51 @@
 			this.tsUnhide = new System.Windows.Forms.ToolStripMenuItem();
 			this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
 			this.tsShowHidden = new System.Windows.Forms.ToolStripMenuItem();
-			this.label33 = new System.Windows.Forms.Label();
-			this.label35 = new System.Windows.Forms.Label();
-			this.label40 = new System.Windows.Forms.Label();
-			this.tmrDelete = new System.Windows.Forms.Timer(this.components);
-			this.label1 = new System.Windows.Forms.Label();
-			this.cboView = new System.Windows.Forms.ComboBox();
-			this.lblTag = new System.Windows.Forms.Label();
-			this.chkHideTargeted = new System.Windows.Forms.CheckBox();
+			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
+			this.cmdLegend = new Desktop.Skinning.SkinnedIcon();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.cboView = new Desktop.Skinning.SkinnedComboBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.lstDialogue = new Desktop.CommonControls.AccordionListView();
+			this.chkHideTargeted = new Desktop.Skinning.SkinnedCheckBox();
+			this.lblTag = new Desktop.Skinning.SkinnedLabel();
 			this.recTag = new Desktop.CommonControls.RecordField();
+			this.label40 = new Desktop.Skinning.SkinnedLabel();
 			this.recTreeTarget = new Desktop.CommonControls.RecordField();
-			this.treeDialogue = new Desktop.CommonControls.DBTreeView();
 			this.tstrDialogue.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// tstrDialogue
 			// 
 			this.tstrDialogue.AutoSize = false;
-			this.tstrDialogue.BackColor = System.Drawing.SystemColors.Control;
 			this.tstrDialogue.CanOverflow = false;
 			this.tstrDialogue.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
 			this.tstrDialogue.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.tsbtnAddDialogue,
+            this.tsRecipe,
+            this.toolStripSeparator3,
+            this.tsRefresh,
+            this.toolStripSeparator2,
             this.tsbtnSplit,
             this.tsbtnRemoveDialogue,
             this.tsConfig});
 			this.tstrDialogue.Location = new System.Drawing.Point(0, 0);
 			this.tstrDialogue.Name = "tstrDialogue";
 			this.tstrDialogue.Padding = new System.Windows.Forms.Padding(2, 2, 3, 2);
-			this.tstrDialogue.Size = new System.Drawing.Size(336, 35);
+			this.tstrDialogue.Size = new System.Drawing.Size(336, 30);
 			this.tstrDialogue.TabIndex = 40;
 			this.tstrDialogue.Text = "toolStrip1";
 			// 
 			// tsbtnAddDialogue
 			// 
-			this.tsbtnAddDialogue.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.tsbtnAddDialogue.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
 			this.tsbtnAddDialogue.DropDown = this.triggerMenu;
+			this.tsbtnAddDialogue.Image = global::SPNATI_Character_Editor.Properties.Resources.Add;
 			this.tsbtnAddDialogue.Name = "tsbtnAddDialogue";
 			this.tsbtnAddDialogue.Overflow = System.Windows.Forms.ToolStripItemOverflow.Never;
 			this.tsbtnAddDialogue.Padding = new System.Windows.Forms.Padding(3);
-			this.tsbtnAddDialogue.Size = new System.Drawing.Size(51, 28);
+			this.tsbtnAddDialogue.Size = new System.Drawing.Size(38, 23);
 			this.tsbtnAddDialogue.Text = "Add";
 			this.tsbtnAddDialogue.ButtonClick += new System.EventHandler(this.tsbtnAddDialogue_ButtonClick);
 			// 
@@ -77,35 +86,68 @@
 			this.triggerMenu.Size = new System.Drawing.Size(36, 4);
 			this.triggerMenu.Opening += new System.ComponentModel.CancelEventHandler(this.triggerMenu_Opening);
 			// 
+			// tsRecipe
+			// 
+			this.tsRecipe.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsRecipe.Image = global::SPNATI_Character_Editor.Properties.Resources.Recipe;
+			this.tsRecipe.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsRecipe.Name = "tsRecipe";
+			this.tsRecipe.Size = new System.Drawing.Size(23, 23);
+			this.tsRecipe.Text = "Use Recipe";
+			this.tsRecipe.Click += new System.EventHandler(this.tsRecipe_Click);
+			// 
+			// toolStripSeparator3
+			// 
+			this.toolStripSeparator3.Name = "toolStripSeparator3";
+			this.toolStripSeparator3.Size = new System.Drawing.Size(6, 26);
+			// 
+			// tsRefresh
+			// 
+			this.tsRefresh.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsRefresh.Image = global::SPNATI_Character_Editor.Properties.Resources.Sort;
+			this.tsRefresh.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsRefresh.Name = "tsRefresh";
+			this.tsRefresh.Size = new System.Drawing.Size(23, 23);
+			this.tsRefresh.Text = "Update sorting";
+			this.tsRefresh.Click += new System.EventHandler(this.tsRefresh_Click);
+			// 
+			// toolStripSeparator2
+			// 
+			this.toolStripSeparator2.Name = "toolStripSeparator2";
+			this.toolStripSeparator2.Size = new System.Drawing.Size(6, 26);
+			// 
 			// tsbtnSplit
 			// 
-			this.tsbtnSplit.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.tsbtnSplit.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsbtnSplit.Image = global::SPNATI_Character_Editor.Properties.Resources.Copy;
 			this.tsbtnSplit.Name = "tsbtnSplit";
 			this.tsbtnSplit.Padding = new System.Windows.Forms.Padding(3);
-			this.tsbtnSplit.Size = new System.Drawing.Size(54, 28);
+			this.tsbtnSplit.Size = new System.Drawing.Size(35, 23);
 			this.tsbtnSplit.Text = "Copy";
 			// 
 			// tsbtnRemoveDialogue
 			// 
 			this.tsbtnRemoveDialogue.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
-			this.tsbtnRemoveDialogue.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.tsbtnRemoveDialogue.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsbtnRemoveDialogue.Image = global::SPNATI_Character_Editor.Properties.Resources.Remove;
 			this.tsbtnRemoveDialogue.Name = "tsbtnRemoveDialogue";
 			this.tsbtnRemoveDialogue.Padding = new System.Windows.Forms.Padding(3);
-			this.tsbtnRemoveDialogue.Size = new System.Drawing.Size(60, 28);
+			this.tsbtnRemoveDialogue.Size = new System.Drawing.Size(26, 23);
 			this.tsbtnRemoveDialogue.Text = "Remove";
 			this.tsbtnRemoveDialogue.Click += new System.EventHandler(this.tsmiRemove_Click);
 			// 
 			// tsConfig
 			// 
-			this.tsConfig.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+			this.tsConfig.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
 			this.tsConfig.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.tsHide,
             this.tsUnhide,
             this.toolStripSeparator1,
             this.tsShowHidden});
+			this.tsConfig.Image = global::SPNATI_Character_Editor.Properties.Resources.Settings;
 			this.tsConfig.ImageTransparentColor = System.Drawing.Color.Magenta;
 			this.tsConfig.Name = "tsConfig";
-			this.tsConfig.Size = new System.Drawing.Size(56, 28);
+			this.tsConfig.Size = new System.Drawing.Size(29, 23);
 			this.tsConfig.Text = "Config";
 			this.tsConfig.DropDownOpening += new System.EventHandler(this.tsConfig_DropDownOpening);
 			// 
@@ -136,81 +178,90 @@
 			this.tsShowHidden.Text = "Show Hidden Cases";
 			this.tsShowHidden.Click += new System.EventHandler(this.tsShowHidden_Click);
 			// 
-			// label33
-			// 
-			this.label33.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-			this.label33.AutoSize = true;
-			this.label33.ForeColor = System.Drawing.Color.Blue;
-			this.label33.Location = new System.Drawing.Point(3, 640);
-			this.label33.Name = "label33";
-			this.label33.Size = new System.Drawing.Size(134, 13);
-			this.label33.TabIndex = 42;
-			this.label33.Text = "Blue: Contains default lines";
-			// 
-			// label35
-			// 
-			this.label35.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-			this.label35.AutoSize = true;
-			this.label35.ForeColor = System.Drawing.Color.Green;
-			this.label35.Location = new System.Drawing.Point(143, 640);
-			this.label35.Name = "label35";
-			this.label35.Size = new System.Drawing.Size(85, 13);
-			this.label35.TabIndex = 43;
-			this.label35.Text = "Green: Targeted";
-			// 
-			// label40
-			// 
-			this.label40.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-			this.label40.AutoSize = true;
-			this.label40.Location = new System.Drawing.Point(3, 594);
-			this.label40.Name = "label40";
-			this.label40.Size = new System.Drawing.Size(62, 13);
-			this.label40.TabIndex = 47;
-			this.label40.Text = "Filter target:";
+			// cmdLegend
+			// 
+			this.cmdLegend.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdLegend.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdLegend.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdLegend.Flat = false;
+			this.cmdLegend.FlatAppearance.BorderSize = 0;
+			this.cmdLegend.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.cmdLegend.Image = global::SPNATI_Character_Editor.Properties.Resources.Legend;
+			this.cmdLegend.Location = new System.Drawing.Point(317, 580);
+			this.cmdLegend.Name = "cmdLegend";
+			this.cmdLegend.Size = new System.Drawing.Size(16, 16);
+			this.cmdLegend.TabIndex = 53;
+			this.toolTip1.SetToolTip(this.cmdLegend, "See Color Legend");
+			this.cmdLegend.UseVisualStyleBackColor = true;
+			this.cmdLegend.Click += new System.EventHandler(this.cmdLegend_Click);
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedPanel1.Controls.Add(this.cboView);
+			this.skinnedPanel1.Controls.Add(this.label1);
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 27);
+			this.skinnedPanel1.Margin = new System.Windows.Forms.Padding(0);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
+			this.skinnedPanel1.Size = new System.Drawing.Size(336, 26);
+			this.skinnedPanel1.TabIndex = 54;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
 			// 
-			// tmrDelete
+			// cboView
 			// 
-			this.tmrDelete.Interval = 1;
-			this.tmrDelete.Tick += new System.EventHandler(this.tmrDelete_Tick);
+			this.cboView.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.cboView.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboView.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboView.BackColor = System.Drawing.Color.White;
+			this.cboView.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboView.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cboView.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboView.FormattingEnabled = true;
+			this.cboView.Location = new System.Drawing.Point(55, 2);
+			this.cboView.Name = "cboView";
+			this.cboView.SelectedIndex = -1;
+			this.cboView.SelectedItem = null;
+			this.cboView.Size = new System.Drawing.Size(278, 21);
+			this.cboView.Sorted = false;
+			this.cboView.TabIndex = 48;
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(3, 35);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.Black;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.PrimaryLight;
+			this.label1.Location = new System.Drawing.Point(3, 5);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(33, 13);
 			this.label1.TabIndex = 49;
 			this.label1.Text = "View:";
 			// 
-			// cboView
+			// lstDialogue
 			// 
-			this.cboView.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+			this.lstDialogue.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.cboView.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
-			this.cboView.FormattingEnabled = true;
-			this.cboView.Items.AddRange(new object[] {
-            "By Stage",
-            "By Case"});
-			this.cboView.Location = new System.Drawing.Point(55, 32);
-			this.cboView.Name = "cboView";
-			this.cboView.Size = new System.Drawing.Size(278, 21);
-			this.cboView.TabIndex = 48;
-			// 
-			// lblTag
-			// 
-			this.lblTag.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-			this.lblTag.AutoSize = true;
-			this.lblTag.Location = new System.Drawing.Point(4, 620);
-			this.lblTag.Name = "lblTag";
-			this.lblTag.Size = new System.Drawing.Size(50, 13);
-			this.lblTag.TabIndex = 51;
-			this.lblTag.Text = "Filter tag:";
+			this.lstDialogue.DataSource = null;
+			this.lstDialogue.Location = new System.Drawing.Point(3, 56);
+			this.lstDialogue.Name = "lstDialogue";
+			this.lstDialogue.SelectedItem = null;
+			this.lstDialogue.ShowIndicators = true;
+			this.lstDialogue.Size = new System.Drawing.Size(330, 519);
+			this.lstDialogue.TabIndex = 52;
+			this.lstDialogue.SelectedIndexChanged += new System.EventHandler<System.EventArgs>(this.lstDialogue_SelectedIndexChanged);
+			this.lstDialogue.KeyDown += new System.Windows.Forms.KeyEventHandler(this.lstDialogue_KeyDown);
 			// 
 			// chkHideTargeted
 			// 
 			this.chkHideTargeted.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
 			this.chkHideTargeted.AutoSize = true;
-			this.chkHideTargeted.Location = new System.Drawing.Point(7, 568);
+			this.chkHideTargeted.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkHideTargeted.Location = new System.Drawing.Point(3, 581);
 			this.chkHideTargeted.Name = "chkHideTargeted";
 			this.chkHideTargeted.Size = new System.Drawing.Size(139, 17);
 			this.chkHideTargeted.TabIndex = 42;
@@ -218,16 +269,31 @@
 			this.chkHideTargeted.UseVisualStyleBackColor = true;
 			this.chkHideTargeted.CheckedChanged += new System.EventHandler(this.chkHideTargeted_CheckedChanged);
 			// 
+			// lblTag
+			// 
+			this.lblTag.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.lblTag.AutoSize = true;
+			this.lblTag.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblTag.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblTag.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblTag.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblTag.Location = new System.Drawing.Point(3, 631);
+			this.lblTag.Name = "lblTag";
+			this.lblTag.Size = new System.Drawing.Size(50, 13);
+			this.lblTag.TabIndex = 51;
+			this.lblTag.Text = "Filter tag:";
+			// 
 			// recTag
 			// 
 			this.recTag.AllowCreate = false;
 			this.recTag.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.recTag.Location = new System.Drawing.Point(71, 617);
+			this.recTag.Location = new System.Drawing.Point(71, 630);
 			this.recTag.Name = "recTag";
 			this.recTag.PlaceholderText = null;
 			this.recTag.Record = null;
 			this.recTag.RecordContext = null;
+			this.recTag.RecordFilter = null;
 			this.recTag.RecordKey = null;
 			this.recTag.RecordType = null;
 			this.recTag.Size = new System.Drawing.Size(262, 20);
@@ -235,16 +301,31 @@
 			this.recTag.UseAutoComplete = true;
 			this.recTag.RecordChanged += new System.EventHandler<Desktop.CommonControls.RecordEventArgs>(this.recTag_RecordChanged);
 			// 
+			// label40
+			// 
+			this.label40.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.label40.AutoSize = true;
+			this.label40.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label40.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label40.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label40.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label40.Location = new System.Drawing.Point(3, 608);
+			this.label40.Name = "label40";
+			this.label40.Size = new System.Drawing.Size(62, 13);
+			this.label40.TabIndex = 47;
+			this.label40.Text = "Filter target:";
+			// 
 			// recTreeTarget
 			// 
 			this.recTreeTarget.AllowCreate = false;
 			this.recTreeTarget.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.recTreeTarget.Location = new System.Drawing.Point(71, 591);
+			this.recTreeTarget.Location = new System.Drawing.Point(71, 604);
 			this.recTreeTarget.Name = "recTreeTarget";
 			this.recTreeTarget.PlaceholderText = null;
 			this.recTreeTarget.Record = null;
 			this.recTreeTarget.RecordContext = null;
+			this.recTreeTarget.RecordFilter = null;
 			this.recTreeTarget.RecordKey = null;
 			this.recTreeTarget.RecordType = null;
 			this.recTreeTarget.Size = new System.Drawing.Size(262, 20);
@@ -252,37 +333,26 @@
 			this.recTreeTarget.UseAutoComplete = true;
 			this.recTreeTarget.RecordChanged += new System.EventHandler<Desktop.CommonControls.RecordEventArgs>(this.recTreeTarget_RecordChanged);
 			// 
-			// treeDialogue
-			// 
-			this.treeDialogue.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.treeDialogue.HideSelection = false;
-			this.treeDialogue.Location = new System.Drawing.Point(3, 59);
-			this.treeDialogue.Name = "treeDialogue";
-			this.treeDialogue.Size = new System.Drawing.Size(330, 500);
-			this.treeDialogue.TabIndex = 41;
-			this.treeDialogue.KeyDown += new System.Windows.Forms.KeyEventHandler(this.treeDialogue_KeyDown);
-			// 
 			// DialogueTree
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.skinnedPanel1);
+			this.Controls.Add(this.cmdLegend);
+			this.Controls.Add(this.lstDialogue);
 			this.Controls.Add(this.chkHideTargeted);
 			this.Controls.Add(this.lblTag);
 			this.Controls.Add(this.recTag);
-			this.Controls.Add(this.label1);
-			this.Controls.Add(this.cboView);
 			this.Controls.Add(this.label40);
 			this.Controls.Add(this.recTreeTarget);
 			this.Controls.Add(this.tstrDialogue);
-			this.Controls.Add(this.treeDialogue);
-			this.Controls.Add(this.label33);
-			this.Controls.Add(this.label35);
 			this.Name = "DialogueTree";
 			this.Size = new System.Drawing.Size(336, 653);
+			this.Load += new System.EventHandler(this.DialogueTree_Load);
 			this.tstrDialogue.ResumeLayout(false);
 			this.tstrDialogue.PerformLayout();
+			this.skinnedPanel1.ResumeLayout(false);
+			this.skinnedPanel1.PerformLayout();
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -295,21 +365,25 @@
 		private System.Windows.Forms.ToolStripSplitButton tsbtnAddDialogue;
 		private System.Windows.Forms.ToolStripDropDownButton tsbtnSplit;
 		private System.Windows.Forms.ToolStripButton tsbtnRemoveDialogue;
-		private Desktop.CommonControls.DBTreeView treeDialogue;
-		private System.Windows.Forms.Label label33;
-		private System.Windows.Forms.Label label35;
-		private System.Windows.Forms.Label label40;
+		private Desktop.Skinning.SkinnedLabel label40;
 		private System.Windows.Forms.ContextMenuStrip triggerMenu;
-		private System.Windows.Forms.Timer tmrDelete;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.ComboBox cboView;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedComboBox cboView;
 		private System.Windows.Forms.ToolStripDropDownButton tsConfig;
 		private System.Windows.Forms.ToolStripMenuItem tsUnhide;
 		private System.Windows.Forms.ToolStripMenuItem tsHide;
 		private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
 		private System.Windows.Forms.ToolStripMenuItem tsShowHidden;
 		private Desktop.CommonControls.RecordField recTag;
-		private System.Windows.Forms.Label lblTag;
-		private System.Windows.Forms.CheckBox chkHideTargeted;
+		private Desktop.Skinning.SkinnedLabel lblTag;
+		private Desktop.Skinning.SkinnedCheckBox chkHideTargeted;
+		private Desktop.CommonControls.AccordionListView lstDialogue;
+		private Desktop.Skinning.SkinnedIcon cmdLegend;
+		private System.Windows.Forms.ToolStripButton tsRecipe;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private System.Windows.Forms.ToolTip toolTip1;
+		private System.Windows.Forms.ToolStripButton tsRefresh;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/DialogueTree.cs b/editor source/SPNATI Character Editor/Controls/DialogueTree.cs
index dc825380804752fb6c15b130e2c187340d81e39e..b0d14225ceb2737040abbafa22787eadc0e7a6ae 100644
--- a/editor source/SPNATI Character Editor/Controls/DialogueTree.cs	
+++ b/editor source/SPNATI Character Editor/Controls/DialogueTree.cs	
@@ -1,12 +1,14 @@
 using Desktop;
 using Desktop.CommonControls;
+using Desktop.Skinning;
+using SPNATI_Character_Editor.Forms;
 using System;
 using System.Collections.Generic;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Controls
 {
-	public partial class DialogueTree : UserControl
+	public partial class DialogueTree : UserControl, ISkinControl
 	{
 		private string LastViewSetting = "TreeView";
 
@@ -32,40 +34,65 @@ namespace SPNATI_Character_Editor.Controls
 		private DialogueNode _selectedNode;
 		private bool _changingViews;
 		private IDialogueTreeView _view;
-		private Queue<TreeNode> _pendingDeletion = new Queue<TreeNode>();
 		private bool _showHidden;
 
 		public DialogueTree()
 		{
 			InitializeComponent();
 
-			recTreeTarget.RecordType = typeof(Character);
-			recTag.RecordType = typeof(Tag);
+			cboView.Items.AddRange(new string[] { "Stage", "Case" });
+		}
+
+		private void DialogueTree_Load(object sender, EventArgs e)
+		{
+			if (!DesignMode)
+			{
+				recTreeTarget.RecordType = typeof(Character);
+				recTag.RecordType = typeof(Tag);
+
+				lstDialogue.FormatRow += LstDialogue_FormatRow;
+				lstDialogue.FormatGroup += LstDialogue_FormatGroup;
+				lstDialogue.RightClick += LstDialogue_RightClick;
+			}
 		}
 
 		public void SetData(Character character)
 		{
 			_character = character;
 			_editorData = CharacterDatabase.GetEditorData(_character);
-			cboView.SelectedIndexChanged += cboView_SelectedIndexChanged;
 			int view = Config.GetInt(LastViewSetting);
 			cboView.SelectedIndex = view;
+			cboView.SelectedIndexChanged += cboView_SelectedIndexChanged;
 			if (_view == null)
 			{
-				_view = new StageView();
-				_view.DeleteNode += _view_DeleteNode;
-				_view.SaveNode += _view_SaveNode;
-				_view.Initialize(treeDialogue, character);
+				if (view == 0)
+				{
+					_view = new StageView();
+				}
+				else
+				{
+					_view = new CaseView();
+				}
+				InitializeView();
 			}
 			tsbtnSplit.DropDown = _view.GetCopyMenu();
+			if (tsbtnSplit.DropDown != null)
+			{
+				SkinManager.Instance.ReskinControl(tsbtnSplit.DropDown, SkinManager.Instance.CurrentSkin);
+			}
 			_character.Behavior.CaseAdded += Behavior_CaseAdded;
 			_character.Behavior.CaseRemoved += Behavior_CaseRemoved;
 			_character.Behavior.CaseModified += Behavior_CaseModified;
 			PopulateTriggerMenu();
 			_view.BuildTree(_showHidden);
+		}
 
-			treeDialogue.BeforeSelect += TreeDialogue_BeforeSelect;
-			treeDialogue.AfterSelect += TreeDialogue_AfterSelect;
+		private void InitializeView()
+		{
+			_view.SaveNode += _view_SaveNode;
+			lstDialogue.DataSource = null;
+			lstDialogue.ClearColumns();
+			_view.Initialize(lstDialogue, _character);
 		}
 
 		private void PopulateTriggerMenu()
@@ -107,7 +134,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		/// <param name="tag">Tag of case to add</param>
 		/// <param name="singleStage">If true, initial stages will only be the current stage. If false, all possible stages will be checked.</param>
-		private void AddAndSelectNewCase(string tag, bool singleStage)
+		private void AddAndSelectNewCase(string tag, string folder, bool singleStage)
 		{
 			Case newCase = new Case(tag);
 			int startStage;
@@ -122,7 +149,8 @@ namespace SPNATI_Character_Editor.Controls
 
 			if (singleStage)
 			{
-				int offset = currentStage - Clothing.MaxLayers;
+				//shift finished stages to the layer-appropriate number
+				int offset = trigger.StartStage - Clothing.MaxLayers;
 				if (offset >= 0)
 				{
 					currentStage = _character.Layers + offset;
@@ -140,6 +168,11 @@ namespace SPNATI_Character_Editor.Controls
 				}
 			}
 
+			if (!string.IsNullOrEmpty(folder) && _editorData != null)
+			{
+				_editorData.SetLabel(newCase, null, null, folder);
+			}
+
 			//Give the host control a chance to setup some properties
 			CreatingCase?.Invoke(this, new CaseCreationEventArgs(newCase));
 
@@ -174,7 +207,7 @@ namespace SPNATI_Character_Editor.Controls
 			CaseSelectionEventArgs args = new CaseSelectionEventArgs(_selectedNode.Stage, selectedCase);
 			SelectedNodeChanging?.Invoke(this, args);
 			_selectedNode = null;
-			treeDialogue.SelectedNode = null;
+			lstDialogue.SelectedItem = null;
 		}
 
 		/// <summary>
@@ -215,7 +248,6 @@ namespace SPNATI_Character_Editor.Controls
 		private void CleanupView()
 		{
 			if (_view == null) { return; }
-			_view.DeleteNode -= _view_DeleteNode;
 			_view.SaveNode -= _view_SaveNode;
 		}
 
@@ -253,7 +285,7 @@ namespace SPNATI_Character_Editor.Controls
 					return workingCase.Target == key || workingCase.AlsoPlaying == key;
 				});
 			}
-			treeDialogue.ExpandAll();
+			lstDialogue.ExpandAll();
 		}
 
 		private void recTag_RecordChanged(object sender, RecordEventArgs e)
@@ -279,54 +311,49 @@ namespace SPNATI_Character_Editor.Controls
 					return workingCase.Filter == key;
 				});
 			}
-			treeDialogue.ExpandAll();
+			lstDialogue.ExpandAll();
 		}
 
-		private void TreeDialogue_BeforeSelect(object sender, TreeViewCancelEventArgs e)
+		private void lstDialogue_SelectedIndexChanged(object sender, EventArgs e)
 		{
 			if (_changingViews) { return; }
 			if (_selectedNode != null)
 			{
-				CaseSelectionEventArgs args = new CaseSelectionEventArgs();
-				args.Stage = _selectedNode.Stage;
-				args.Case = _selectedNode.Case;
-				SelectedNodeChanging?.Invoke(this, args);
+				CaseSelectionEventArgs selectionArgs = new CaseSelectionEventArgs();
+				selectionArgs.Stage = _selectedNode.Stage;
+				selectionArgs.Case = _selectedNode.Case;
+				//DialogueNode targetNode = lstDialogue.SelectedItem as DialogueNode;
+				SelectedNodeChanging?.Invoke(this, selectionArgs);
+
+				//the above might have shuffled items around, so make sure we actually have the targetNode selected
+				//if (targetNode != lstDialogue.SelectedItem)
+				//{
+				//	_selectedNode = null;
+				//	lstDialogue.SelectedItem = targetNode;
+				//	return; //another event will be raised
+				//}
 			}
-		}
 
-		private void TreeDialogue_AfterSelect(object sender, TreeViewEventArgs e)
-		{
-			if (_changingViews) { return; }
-			TreeNode node = treeDialogue.SelectedNode;
-			DialogueNode wrapper = node?.Tag as DialogueNode;
-			_selectedNode = wrapper;
+			DialogueNode node = lstDialogue.SelectedItem as DialogueNode;
+			_selectedNode = node;
 
 			CaseSelectionEventArgs args = new CaseSelectionEventArgs();
-			if (wrapper != null)
+			if (node != null)
 			{
-				args.Stage = wrapper.Stage;
-				args.Case = wrapper.Case;
+				args.Stage = node.Stage;
+				args.Case = node.Case;
 			}
 			SelectedNodeChanged?.Invoke(this, args);
 
-			if (wrapper == null)
+			if (node == null)
 			{
 				tsbtnRemoveDialogue.Enabled = false;
 				tsbtnSplit.Enabled = false;
 				return;
 			}
 
-			switch (wrapper.NodeType)
-			{
-				case NodeType.Case:
-					tsbtnRemoveDialogue.Enabled = true;
-					tsbtnSplit.Enabled = true;
-					break;
-				case NodeType.Stage:
-					tsbtnRemoveDialogue.Enabled = false;
-					tsbtnSplit.Enabled = false;
-					break;
-			}
+			tsbtnRemoveDialogue.Enabled = true;
+			tsbtnSplit.Enabled = true;
 		}
 
 		private void Behavior_CaseModified(object sender, Case modifiedCase)
@@ -354,7 +381,8 @@ namespace SPNATI_Character_Editor.Controls
 		/// <param name="e"></param>
 		private void tsbtnAddDialogue_ButtonClick(object sender, EventArgs e)
 		{
-			string tag = _view.AddingCase();
+			string folder;
+			string tag = _view.AddingCase(out folder);
 			if (tag == null)
 			{
 				return;
@@ -365,7 +393,7 @@ namespace SPNATI_Character_Editor.Controls
 			}
 			else
 			{
-				AddAndSelectNewCase(tag, true);
+				AddAndSelectNewCase(tag, folder, true);
 			}
 		}
 
@@ -375,7 +403,7 @@ namespace SPNATI_Character_Editor.Controls
 				return;
 
 			string tag = ((ToolStripMenuItem)sender).Name;
-			AddAndSelectNewCase(tag, false);
+			AddAndSelectNewCase(tag, "", false);
 		}
 
 		private void tsmiRemove_Click(object sender, EventArgs e)
@@ -383,7 +411,7 @@ namespace SPNATI_Character_Editor.Controls
 			DeleteSelectedCase();
 		}
 
-		private void treeDialogue_KeyDown(object sender, KeyEventArgs e)
+		private void lstDialogue_KeyDown(object sender, KeyEventArgs e)
 		{
 			if (e.KeyCode == Keys.Delete)
 			{
@@ -391,22 +419,6 @@ namespace SPNATI_Character_Editor.Controls
 			}
 		}
 
-		private void tmrDelete_Tick(object sender, EventArgs e)
-		{
-			tmrDelete.Enabled = false;
-			while (_pendingDeletion.Count > 0)
-			{
-				TreeNode node = _pendingDeletion.Dequeue();
-				node.Remove();
-			}
-		}
-
-		private void _view_DeleteNode(object sender, TreeNode node)
-		{
-			_pendingDeletion.Enqueue(node);
-			tmrDelete.Enabled = true;
-		}
-
 		private void _view_SaveNode(object sender, EventArgs e)
 		{
 			LeaveNode();
@@ -428,9 +440,7 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				_view = new CaseView();
 			}
-			_view.DeleteNode += _view_DeleteNode;
-			_view.SaveNode += _view_SaveNode;
-			_view.Initialize(treeDialogue, _character);
+			InitializeView();
 			tsbtnSplit.DropDown = _view.GetCopyMenu();
 			_view.SetFilter(null);
 			_view.BuildTree(_showHidden);
@@ -440,8 +450,7 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void triggerMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e)
 		{
-			TreeNode selected = treeDialogue.SelectedNode;
-			DialogueNode node = selected?.Tag as DialogueNode;
+			DialogueNode node = lstDialogue.SelectedItem as DialogueNode;
 
 			foreach (ToolStripMenuItem group in triggerMenu.Items)
 			{
@@ -465,7 +474,7 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void tsConfig_DropDownOpening(object sender, EventArgs e)
 		{
-			DialogueNode node = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode node = lstDialogue.SelectedItem as DialogueNode;
 			if (node == null || node.Case == null)
 			{
 				tsHide.Enabled = false;
@@ -482,7 +491,7 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void tsHide_Click(object sender, EventArgs e)
 		{
-			DialogueNode node = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode node = lstDialogue.SelectedItem as DialogueNode;
 			Case selectedCase = node?.Case;
 			if (selectedCase == null) { return; }
 
@@ -492,7 +501,7 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void tsUnhide_Click(object sender, EventArgs e)
 		{
-			DialogueNode node = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode node = lstDialogue.SelectedItem as DialogueNode;
 			Case selectedCase = node?.Case;
 			if (selectedCase == null) { return; }
 
@@ -505,6 +514,61 @@ namespace SPNATI_Character_Editor.Controls
 			_showHidden = !_showHidden;
 			RegenerateTree();
 		}
+
+		private void LstDialogue_FormatGroup(object sender, FormatGroupEventArgs e)
+		{
+			_view?.FormatGroup(e);
+		}
+
+		private void LstDialogue_FormatRow(object sender, FormatRowEventArgs e)
+		{
+			_view?.FormatRow(e);
+		}
+
+		private void LstDialogue_RightClick(object sender, AccordionListViewEventArgs e)
+		{
+			ContextMenuStrip strip = _view?.ShowContextMenu(e);
+			if (strip != null)
+			{
+				strip.Show(Cursor.Position);
+			}
+		}
+
+		private void cmdLegend_Click(object sender, EventArgs e)
+		{
+			new DialogueLegend().ShowDialog();
+		}
+
+		private void tsRecipe_Click(object sender, EventArgs e)
+		{
+			Recipe recipe = RecordLookup.DoLookup(typeof(Recipe), "", false, null) as Recipe;
+			if (recipe != null)
+			{
+				Case instance = recipe.AddToCharacter(_character);
+				SelectNode(instance.Stages[0], instance);
+			}
+		}
+
+		private void tsRefresh_Click(object sender, EventArgs e)
+		{
+			int currentStage = _selectedNode?.Stage?.Id ?? -1;
+			Case currentCase = _selectedNode?.Case;
+			_view?.Sort();
+			Invalidate(true);
+			if (currentCase != null && currentStage >= 0)
+			{
+				_view.SelectNode(currentStage, currentCase);
+			}
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			SkinManager.Instance.ReskinControl(triggerMenu, skin);
+			if (tsbtnSplit.DropDown != null)
+			{
+				SkinManager.Instance.ReskinControl(tsbtnSplit.DropDown, skin);
+			}
+		}
 	}
 
 	public class CaseSelectionEventArgs : EventArgs
diff --git a/editor source/SPNATI Character Editor/Controls/DialogueTree.resx b/editor source/SPNATI Character Editor/Controls/DialogueTree.resx
index ba2e7649aa29332b3162bbff367b8bd5a44b6da8..05964883c13da5b3f03b15105197fd014824600f 100644
--- a/editor source/SPNATI Character Editor/Controls/DialogueTree.resx	
+++ b/editor source/SPNATI Character Editor/Controls/DialogueTree.resx	
@@ -123,7 +123,7 @@
   <metadata name="triggerMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>724, 17</value>
   </metadata>
-  <metadata name="tmrDelete.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+  <metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>844, 17</value>
   </metadata>
 </root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleControl.Designer.cs
deleted file mode 100644
index 6e3d9dc7a33d976fd299b3f682d16ded6a5e4ee1..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleControl.Designer.cs	
+++ /dev/null
@@ -1,133 +0,0 @@
-namespace SPNATI_Character_Editor
-{
-	partial class CharacterCollectibleControl
-	{
-		/// <summary> 
-		/// Required designer variable.
-		/// </summary>
-		private System.ComponentModel.IContainer components = null;
-
-		/// <summary> 
-		/// Clean up any resources being used.
-		/// </summary>
-		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
-		protected override void Dispose(bool disposing)
-		{
-			if (disposing && (components != null))
-			{
-				components.Dispose();
-			}
-			base.Dispose(disposing);
-		}
-
-		#region Component Designer generated code
-
-		/// <summary> 
-		/// Required method for Designer support - do not modify 
-		/// the contents of this method with the code editor.
-		/// </summary>
-		private void InitializeComponent()
-		{
-			this.recItem = new Desktop.CommonControls.RecordField();
-			this.label1 = new System.Windows.Forms.Label();
-			this.radUnlocked = new System.Windows.Forms.RadioButton();
-			this.radLocked = new System.Windows.Forms.RadioButton();
-			this.label2 = new System.Windows.Forms.Label();
-			this.recCharacter = new Desktop.CommonControls.RecordField();
-			this.SuspendLayout();
-			// 
-			// recItem
-			// 
-			this.recItem.AllowCreate = false;
-			this.recItem.Location = new System.Drawing.Point(241, 1);
-			this.recItem.Name = "recItem";
-			this.recItem.PlaceholderText = null;
-			this.recItem.Record = null;
-			this.recItem.RecordContext = null;
-			this.recItem.RecordKey = null;
-			this.recItem.RecordType = null;
-			this.recItem.Size = new System.Drawing.Size(112, 20);
-			this.recItem.TabIndex = 1;
-			this.recItem.UseAutoComplete = false;
-			// 
-			// label1
-			// 
-			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(180, 4);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(58, 13);
-			this.label1.TabIndex = 1;
-			this.label1.Text = "Collectible:";
-			// 
-			// radUnlocked
-			// 
-			this.radUnlocked.AutoSize = true;
-			this.radUnlocked.Location = new System.Drawing.Point(357, 2);
-			this.radUnlocked.Name = "radUnlocked";
-			this.radUnlocked.Size = new System.Drawing.Size(71, 17);
-			this.radUnlocked.TabIndex = 2;
-			this.radUnlocked.TabStop = true;
-			this.radUnlocked.Text = "Unlocked";
-			this.radUnlocked.UseVisualStyleBackColor = true;
-			// 
-			// radLocked
-			// 
-			this.radLocked.AutoSize = true;
-			this.radLocked.Location = new System.Drawing.Point(427, 2);
-			this.radLocked.Name = "radLocked";
-			this.radLocked.Size = new System.Drawing.Size(61, 17);
-			this.radLocked.TabIndex = 3;
-			this.radLocked.TabStop = true;
-			this.radLocked.Text = "Locked";
-			this.radLocked.UseVisualStyleBackColor = true;
-			// 
-			// label2
-			// 
-			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(3, 4);
-			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(56, 13);
-			this.label2.TabIndex = 5;
-			this.label2.Text = "Character:";
-			// 
-			// recCharacter
-			// 
-			this.recCharacter.AllowCreate = false;
-			this.recCharacter.Location = new System.Drawing.Point(65, 1);
-			this.recCharacter.Name = "recCharacter";
-			this.recCharacter.PlaceholderText = null;
-			this.recCharacter.Record = null;
-			this.recCharacter.RecordContext = null;
-			this.recCharacter.RecordKey = null;
-			this.recCharacter.RecordType = null;
-			this.recCharacter.Size = new System.Drawing.Size(112, 20);
-			this.recCharacter.TabIndex = 0;
-			this.recCharacter.UseAutoComplete = false;
-			// 
-			// CharacterCollectibleControl
-			// 
-			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.label2);
-			this.Controls.Add(this.recCharacter);
-			this.Controls.Add(this.radLocked);
-			this.Controls.Add(this.radUnlocked);
-			this.Controls.Add(this.label1);
-			this.Controls.Add(this.recItem);
-			this.Name = "CharacterCollectibleControl";
-			this.Size = new System.Drawing.Size(521, 21);
-			this.ResumeLayout(false);
-			this.PerformLayout();
-
-		}
-
-		#endregion
-
-		private Desktop.CommonControls.RecordField recItem;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.RadioButton radUnlocked;
-		private System.Windows.Forms.RadioButton radLocked;
-		private System.Windows.Forms.Label label2;
-		private Desktop.CommonControls.RecordField recCharacter;
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleControl.cs
deleted file mode 100644
index 76f3836be86cdee38ae586496a2532ddd63286c7..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleControl.cs	
+++ /dev/null
@@ -1,141 +0,0 @@
-using SPNATI_Character_Editor.DataStructures;
-using System.Text.RegularExpressions;
-using System.Collections.Generic;
-
-namespace SPNATI_Character_Editor
-{
-	public partial class CharacterCollectibleControl : SubVariableControl
-	{
-		private ExpressionTest _expression;
-		
-		public CharacterCollectibleControl()
-		{
-			InitializeComponent();
-
-			recCharacter.RecordType = typeof(Character);
-			recItem.RecordType = typeof(Collectible);
-		}
-
-		protected override void OnBoundData()
-		{
-			_expression = GetValue() as ExpressionTest;
-			
-			recItem.RecordKey = null;
-
-			string pattern = @"~(.*)\.collectible\.([^~]*)~";
-			Match match = Regex.Match(_expression.Expression, pattern);
-			if (match.Success)
-			{
-				string id = match.Groups[1].Value;
-				string key = match.Groups[2].Value;
-
-				Character character = CharacterDatabase.GetById(id);
-				if (character == null)
-				{
-					//default to AlsoPlaying
-					Case data = Data as Case;
-					character = CharacterDatabase.Get(data.AlsoPlaying);
-				}
-
-				recItem.RecordContext = character;
-				recCharacter.Record = character;
-
-				if (!string.IsNullOrEmpty(key) && key != "*")
-				{
-					recItem.RecordKey = key;
-				}
-			}
-
-			radLocked.Checked = _expression.Value == "false";
-			radUnlocked.Checked = _expression.Value != "false";
-			OnAddedToRow();
-		}
-		
-		public override void OnAddedToRow()
-		{
-			OnChangeLabel("Also Playing Collectible");
-		}
-
-		protected override void AddHandlers()
-		{
-			recCharacter.RecordChanged += RecCharacter_RecordChanged;
-			recItem.RecordChanged += RecField_RecordChanged;
-			radLocked.CheckedChanged += RadLocked_CheckedChanged;
-			radUnlocked.CheckedChanged += RadLocked_CheckedChanged;
-		}
-
-		protected override void RemoveHandlers()
-		{
-			recCharacter.RecordChanged -= RecCharacter_RecordChanged;
-			recItem.RecordChanged -= RecField_RecordChanged;
-			radLocked.CheckedChanged -= RadLocked_CheckedChanged;
-			radUnlocked.CheckedChanged -= RadLocked_CheckedChanged;
-		}
-
-		public override void ApplyMacro(List<string> values)
-		{
-			//macros should never be applied directly to a subcontrol
-		}
-
-		public override void BuildMacro(List<string> values)
-		{
-			Save();
-			values.Add(_expression.Expression);
-			values.Add(_expression.Operator);
-			values.Add(_expression.Value);
-		}
-
-		private void RecCharacter_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
-		{
-			Character character = recCharacter.Record as Character;
-			recItem.RecordContext = character;
-			recItem.RecordKey = null;
-			Save();
-		}
-
-		private void RecField_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
-		{
-			Save();	
-		}
-		private void RadLocked_CheckedChanged(object sender, System.EventArgs e)
-		{
-			Save();
-		}
-
-		public override void Save()
-		{
-			string character = recCharacter.RecordKey;
-			string id = "_";
-			if (!string.IsNullOrEmpty(character))
-			{
-				id = CharacterDatabase.GetId(character);
-			}
-
-			string key = recItem.RecordKey;
-			if (string.IsNullOrEmpty(key))
-			{
-				key = "*";
-			}
-			string expression = $"~{id}.collectible.*~".Replace("*", key);
-			_expression.Expression = expression;
-			_expression.Operator = "==";
-			if (radLocked.Checked)
-			{
-				_expression.Value = "false";
-			}
-			else
-			{
-				_expression.Value = "true";
-			}
-
-			base.Save();
-		}
-
-		private enum TargetMode
-		{
-			Self,
-			AlsoPlaying,
-			Target
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleCountControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleCountControl.Designer.cs
deleted file mode 100644
index 8a9b13717a8dd303add6eb020be856eabf60a9d3..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleCountControl.Designer.cs	
+++ /dev/null
@@ -1,136 +0,0 @@
-namespace SPNATI_Character_Editor
-{
-	partial class CharacterCollectibleCountControl
-	{
-		/// <summary> 
-		/// Required designer variable.
-		/// </summary>
-		private System.ComponentModel.IContainer components = null;
-
-		/// <summary> 
-		/// Clean up any resources being used.
-		/// </summary>
-		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
-		protected override void Dispose(bool disposing)
-		{
-			if (disposing && (components != null))
-			{
-				components.Dispose();
-			}
-			base.Dispose(disposing);
-		}
-
-		#region Component Designer generated code
-
-		/// <summary> 
-		/// Required method for Designer support - do not modify 
-		/// the contents of this method with the code editor.
-		/// </summary>
-		private void InitializeComponent()
-		{
-			this.recItem = new Desktop.CommonControls.RecordField();
-			this.label1 = new System.Windows.Forms.Label();
-			this.cboOperator = new System.Windows.Forms.ComboBox();
-			this.valCounter = new System.Windows.Forms.NumericUpDown();
-			this.label2 = new System.Windows.Forms.Label();
-			this.recCharacter = new Desktop.CommonControls.RecordField();
-			((System.ComponentModel.ISupportInitialize)(this.valCounter)).BeginInit();
-			this.SuspendLayout();
-			// 
-			// recItem
-			// 
-			this.recItem.AllowCreate = false;
-			this.recItem.Location = new System.Drawing.Point(241, 1);
-			this.recItem.Name = "recItem";
-			this.recItem.PlaceholderText = null;
-			this.recItem.Record = null;
-			this.recItem.RecordContext = null;
-			this.recItem.RecordKey = null;
-			this.recItem.RecordType = null;
-			this.recItem.Size = new System.Drawing.Size(110, 20);
-			this.recItem.TabIndex = 0;
-			this.recItem.UseAutoComplete = false;
-			// 
-			// label1
-			// 
-			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(180, 4);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(58, 13);
-			this.label1.TabIndex = 1;
-			this.label1.Text = "Collectible:";
-			// 
-			// cboOperator
-			// 
-			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
-			this.cboOperator.FormattingEnabled = true;
-			this.cboOperator.Items.AddRange(new object[] {
-            "==",
-            "<=",
-            "<",
-            ">",
-            ">=",
-            "!="});
-			this.cboOperator.Location = new System.Drawing.Point(357, 0);
-			this.cboOperator.Name = "cboOperator";
-			this.cboOperator.Size = new System.Drawing.Size(39, 21);
-			this.cboOperator.TabIndex = 2;
-			// 
-			// valCounter
-			// 
-			this.valCounter.Location = new System.Drawing.Point(402, 0);
-			this.valCounter.Name = "valCounter";
-			this.valCounter.Size = new System.Drawing.Size(52, 20);
-			this.valCounter.TabIndex = 3;
-			// 
-			// label2
-			// 
-			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(3, 4);
-			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(56, 13);
-			this.label2.TabIndex = 7;
-			this.label2.Text = "Character:";
-			// 
-			// recCharacter
-			// 
-			this.recCharacter.AllowCreate = false;
-			this.recCharacter.Location = new System.Drawing.Point(65, 1);
-			this.recCharacter.Name = "recCharacter";
-			this.recCharacter.PlaceholderText = null;
-			this.recCharacter.Record = null;
-			this.recCharacter.RecordContext = null;
-			this.recCharacter.RecordKey = null;
-			this.recCharacter.RecordType = null;
-			this.recCharacter.Size = new System.Drawing.Size(112, 20);
-			this.recCharacter.TabIndex = 6;
-			this.recCharacter.UseAutoComplete = false;
-			// 
-			// CharacterCollectibleCountControl
-			// 
-			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.label2);
-			this.Controls.Add(this.recCharacter);
-			this.Controls.Add(this.valCounter);
-			this.Controls.Add(this.cboOperator);
-			this.Controls.Add(this.label1);
-			this.Controls.Add(this.recItem);
-			this.Name = "CharacterCollectibleCountControl";
-			this.Size = new System.Drawing.Size(457, 21);
-			((System.ComponentModel.ISupportInitialize)(this.valCounter)).EndInit();
-			this.ResumeLayout(false);
-			this.PerformLayout();
-
-		}
-
-		#endregion
-
-		private Desktop.CommonControls.RecordField recItem;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.ComboBox cboOperator;
-		private System.Windows.Forms.NumericUpDown valCounter;
-		private System.Windows.Forms.Label label2;
-		private Desktop.CommonControls.RecordField recCharacter;
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleCountControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleCountControl.cs
deleted file mode 100644
index 470d3a20e6a9b8a9a323142d5f70af85e0520d02..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterCollectibleCountControl.cs	
+++ /dev/null
@@ -1,143 +0,0 @@
-using SPNATI_Character_Editor.DataStructures;
-using System.Text.RegularExpressions;
-using System.Collections.Generic;
-using System;
-using SPNATI_Character_Editor.Providers;
-
-namespace SPNATI_Character_Editor
-{
-	public partial class CharacterCollectibleCountControl : SubVariableControl
-	{
-		private ExpressionTest _expression;
-
-		public CharacterCollectibleCountControl()
-		{
-			InitializeComponent();
-
-			recCharacter.RecordType = typeof(Character);
-			recItem.RecordType = typeof(Collectible);
-		}
-
-		protected override void OnBoundData()
-		{
-			_expression = GetValue() as ExpressionTest;
-
-			recItem.RecordKey = null;
-
-			string pattern = @"~(.*)\.collectible\.([^.~]*).counter~";
-			Match match = Regex.Match(_expression.Expression, pattern);
-			if (match.Success)
-			{
-				string id = match.Groups[1].Value;
-				string key = match.Groups[2].Value;
-
-				Character character = CharacterDatabase.GetById(id);
-				if (character == null)
-				{
-					//default to AlsoPlaying
-					Case data = Data as Case;
-					character = CharacterDatabase.Get(data.AlsoPlaying);
-				}
-
-				recItem.RecordContext = character;
-				recCharacter.Record = character;
-
-				if (!string.IsNullOrEmpty(key) && key != "*")
-				{
-					recItem.RecordKey = key;
-					if (recItem.RecordKey == null)
-					{
-						CollectibleProvider provider = new CollectibleProvider();
-						provider.Create(key);
-						recItem.RecordKey = key;
-					}
-				}
-			}
-
-			try
-			{
-				cboOperator.SelectedItem = _expression.Operator ?? "==";
-			}
-			catch
-			{
-				cboOperator.SelectedItem = "==";
-			}
-			int count;
-			int.TryParse(_expression.Value, out count);
-			valCounter.Value = Math.Max(valCounter.Minimum, Math.Min(valCounter.Maximum, count));
-			OnAddedToRow();
-		}
-
-		public override void OnAddedToRow()
-		{
-			OnChangeLabel("Also Playing Collectible (Counter)");
-		}
-
-		protected override void AddHandlers()
-		{
-			recCharacter.RecordChanged += RecCharacter_RecordChanged;
-			recItem.RecordChanged += RecField_RecordChanged;
-			cboOperator.SelectedIndexChanged += Field_ValueChanged;
-			valCounter.ValueChanged += Field_ValueChanged;
-		}
-
-		protected override void RemoveHandlers()
-		{
-			recCharacter.RecordChanged -= RecCharacter_RecordChanged;
-			recItem.RecordChanged -= RecField_RecordChanged;
-			cboOperator.SelectedIndexChanged -= Field_ValueChanged;
-			valCounter.ValueChanged -= Field_ValueChanged;
-		}
-
-		public override void ApplyMacro(List<string> values)
-		{
-			//macros should never be applied directly to a subcontrol
-		}
-
-		public override void BuildMacro(List<string> values)
-		{
-			Save();
-			values.Add(_expression.Expression);
-			values.Add(_expression.Operator);
-			values.Add(_expression.Value);
-		}
-
-		private void RecCharacter_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
-		{
-			Character character = recCharacter.Record as Character;
-			recItem.RecordContext = character;
-			recItem.RecordKey = null;
-			Save();
-		}
-
-		private void RecField_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
-		{
-			Save();	
-		}
-		private void Field_ValueChanged(object sender, System.EventArgs e)
-		{
-			Save();
-		}
-
-		public override void Save()
-		{
-			string character = recCharacter.RecordKey;
-			string id = "_";
-			if (!string.IsNullOrEmpty(character))
-			{
-				id = CharacterDatabase.GetId(character);
-			}
-
-			string key = recItem.RecordKey;
-			if (string.IsNullOrEmpty(key))
-			{
-				key = "*";
-			}
-			string expression = $"~{id}.collectible.*.counter~".Replace("*", key);
-			_expression.Expression = expression;
-			_expression.Operator = cboOperator.Text;
-			_expression.Value = valCounter.Value.ToString();
-
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterPersistentMarkerControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/CharacterPersistentMarkerControl.Designer.cs
deleted file mode 100644
index 8baab663be9140d26d514a859e4dcc2d21237d16..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterPersistentMarkerControl.Designer.cs	
+++ /dev/null
@@ -1,135 +0,0 @@
-namespace SPNATI_Character_Editor
-{
-	partial class CharacterPersistentMarkerControl
-	{
-		/// <summary> 
-		/// Required designer variable.
-		/// </summary>
-		private System.ComponentModel.IContainer components = null;
-
-		/// <summary> 
-		/// Clean up any resources being used.
-		/// </summary>
-		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
-		protected override void Dispose(bool disposing)
-		{
-			if (disposing && (components != null))
-			{
-				components.Dispose();
-			}
-			base.Dispose(disposing);
-		}
-
-		#region Component Designer generated code
-
-		/// <summary> 
-		/// Required method for Designer support - do not modify 
-		/// the contents of this method with the code editor.
-		/// </summary>
-		private void InitializeComponent()
-		{
-			this.recItem = new Desktop.CommonControls.RecordField();
-			this.txtValue = new System.Windows.Forms.TextBox();
-			this.cboOperator = new System.Windows.Forms.ComboBox();
-			this.label2 = new System.Windows.Forms.Label();
-			this.recCharacter = new Desktop.CommonControls.RecordField();
-			this.label1 = new System.Windows.Forms.Label();
-			this.SuspendLayout();
-			// 
-			// recField
-			// 
-			this.recItem.AllowCreate = true;
-			this.recItem.Location = new System.Drawing.Point(226, 1);
-			this.recItem.Name = "recField";
-			this.recItem.PlaceholderText = null;
-			this.recItem.Record = null;
-			this.recItem.RecordContext = null;
-			this.recItem.RecordKey = null;
-			this.recItem.RecordType = null;
-			this.recItem.Size = new System.Drawing.Size(109, 20);
-			this.recItem.TabIndex = 0;
-			this.recItem.UseAutoComplete = false;
-			// 
-			// txtValue
-			// 
-			this.txtValue.Location = new System.Drawing.Point(399, 0);
-			this.txtValue.Name = "txtValue";
-			this.txtValue.Size = new System.Drawing.Size(100, 20);
-			this.txtValue.TabIndex = 5;
-			// 
-			// cboOperator
-			// 
-			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
-			this.cboOperator.FormattingEnabled = true;
-			this.cboOperator.Items.AddRange(new object[] {
-            "",
-            "==",
-            "!=",
-            "<",
-            ">",
-            "<=",
-            ">="});
-			this.cboOperator.Location = new System.Drawing.Point(339, 0);
-			this.cboOperator.Name = "cboOperator";
-			this.cboOperator.Size = new System.Drawing.Size(56, 21);
-			this.cboOperator.TabIndex = 4;
-			// 
-			// label2
-			// 
-			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(2, 3);
-			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(56, 13);
-			this.label2.TabIndex = 8;
-			this.label2.Text = "Character:";
-			// 
-			// recCharacter
-			// 
-			this.recCharacter.AllowCreate = false;
-			this.recCharacter.Location = new System.Drawing.Point(64, 0);
-			this.recCharacter.Name = "recCharacter";
-			this.recCharacter.PlaceholderText = null;
-			this.recCharacter.Record = null;
-			this.recCharacter.RecordContext = null;
-			this.recCharacter.RecordKey = null;
-			this.recCharacter.RecordType = null;
-			this.recCharacter.Size = new System.Drawing.Size(112, 20);
-			this.recCharacter.TabIndex = 6;
-			this.recCharacter.UseAutoComplete = false;
-			// 
-			// label1
-			// 
-			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(179, 3);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(43, 13);
-			this.label1.TabIndex = 7;
-			this.label1.Text = "Marker:";
-			// 
-			// CharacterPersistentMarkerControl
-			// 
-			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.label2);
-			this.Controls.Add(this.recCharacter);
-			this.Controls.Add(this.label1);
-			this.Controls.Add(this.txtValue);
-			this.Controls.Add(this.cboOperator);
-			this.Controls.Add(this.recItem);
-			this.Name = "CharacterPersistentMarkerControl";
-			this.Size = new System.Drawing.Size(605, 21);
-			this.ResumeLayout(false);
-			this.PerformLayout();
-
-		}
-
-		#endregion
-
-		private Desktop.CommonControls.RecordField recItem;
-		private System.Windows.Forms.TextBox txtValue;
-		private System.Windows.Forms.ComboBox cboOperator;
-		private System.Windows.Forms.Label label2;
-		private Desktop.CommonControls.RecordField recCharacter;
-		private System.Windows.Forms.Label label1;
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterPersistentMarkerControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/CharacterPersistentMarkerControl.cs
deleted file mode 100644
index f7cc6f9028f3a8ab86de68ebd1f122ef6e4f33fe..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterPersistentMarkerControl.cs	
+++ /dev/null
@@ -1,168 +0,0 @@
-using Desktop;
-using Desktop.CommonControls;
-using System;
-using System.Text.RegularExpressions;
-using System.Collections.Generic;
-
-namespace SPNATI_Character_Editor
-{
-	public partial class CharacterPersistentMarkerControl : SubVariableControl
-	{
-		private ExpressionTest _expression;
-
-		public CharacterPersistentMarkerControl()
-		{
-			InitializeComponent();
-
-			recCharacter.RecordType = typeof(Character);
-			recItem.RecordType = typeof(Marker);
-			recItem.RecordFilter = FilterPrivateMarkers;
-		}
-
-		public override void BuildMacro(List<string> values)
-		{
-			Save();
-			values.Add(_expression.Expression);
-			values.Add(_expression.Operator);
-			values.Add(_expression.Value);
-		}
-
-		public override void OnAddedToRow()
-		{
-			OnChangeLabel("Also Playing Marker (Persistent)");
-		}
-
-		private bool FilterPrivateMarkers(IRecord record)
-		{
-			Marker marker = record as Marker;
-			return marker.Scope == MarkerScope.Public;
-		}
-
-		protected override void OnBoundData()
-		{
-			_expression = GetValue() as ExpressionTest;
-
-			recItem.RecordKey = null;
-
-			string pattern = @"~(.*)\.persistent\.([^~]*)~";
-			Match match = Regex.Match(_expression.Expression, pattern);
-			if (match.Success)
-			{
-				string id = match.Groups[1].Value;
-				string key = match.Groups[2].Value;
-
-				Character character = CharacterDatabase.GetById(id);
-				if (character == null)
-				{
-					//default to AlsoPlaying
-					Case data = Data as Case;
-					character = CharacterDatabase.Get(data.AlsoPlaying);
-				}
-
-				recItem.RecordContext = character;
-				recCharacter.Record = character;
-
-				if (!string.IsNullOrEmpty(key) && key != "*")
-				{
-					recItem.RecordKey = key;
-				}
-			}
-
-			cboOperator.Text = _expression.Operator;
-			txtValue.Text = _expression.Value;
-			OnAddedToRow();
-		}
-
-		private void SetTargetContext()
-		{
-			Case context = Data as Case;
-			string target = context.Target;
-			if (!string.IsNullOrEmpty(target))
-			{
-				Character targetChar = CharacterDatabase.Get(target);
-				recItem.RecordContext = targetChar;
-			}
-			else
-			{
-				recItem.RecordContext = null;
-			}
-		}
-
-		protected override void RemoveHandlers()
-		{
-			recCharacter.RecordChanged -= RecCharacter_RecordChanged;
-			recItem.RecordChanged -= RecordChanged;
-			cboOperator.SelectedIndexChanged -= ValueChanged;
-			txtValue.TextChanged -= ValueChanged;
-		}
-
-		protected override void AddHandlers()
-		{
-			recCharacter.RecordChanged += RecCharacter_RecordChanged;
-			recItem.RecordChanged += RecordChanged;
-			cboOperator.SelectedIndexChanged += ValueChanged;
-			txtValue.TextChanged += ValueChanged;
-		}
-
-		public override void Clear()
-		{
-			RemoveHandlers();
-			recCharacter.RecordKey = null;
-			recItem.RecordKey = null;
-			cboOperator.SelectedIndex = 0;
-			txtValue.Text = "";
-			AddHandlers();
-			Save();
-		}
-
-		public override void Save()
-		{
-			string character = recCharacter.RecordKey;
-			string id = "_";
-			if (!string.IsNullOrEmpty(character))
-			{
-				id = CharacterDatabase.GetId(character);
-			}
-
-			string key = recItem.RecordKey;
-			if (string.IsNullOrEmpty(key))
-			{
-				key = "*";
-			}
-
-			string expression = $"~{id}.persistent.*~".Replace("*", key);
-			_expression.Expression = expression;
-
-			string op = cboOperator.SelectedItem?.ToString();
-			if (string.IsNullOrEmpty(op))
-			{
-				op = ">";
-			}
-			_expression.Operator = op;
-
-			string value = txtValue.Text;
-			if (string.IsNullOrEmpty(value))
-			{
-				value = "0";
-			}
-			_expression.Value = value;
-		}
-
-		private void ValueChanged(object sender, EventArgs e)
-		{
-			Save();
-		}
-		private void RecCharacter_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
-		{
-			Character character = recCharacter.Record as Character;
-			recItem.RecordContext = character;
-			recItem.RecordKey = null;
-			Save();
-		}
-
-		private void RecordChanged(object sender, RecordEventArgs record)
-		{
-			Save();
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterTagControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/CharacterTagControl.Designer.cs
deleted file mode 100644
index 0ef29d5d254c2272dd750f5bd4d0c158f4ddc7fb..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterTagControl.Designer.cs	
+++ /dev/null
@@ -1,118 +0,0 @@
-namespace SPNATI_Character_Editor
-{
-	partial class CharacterTagControl
-	{
-		/// <summary> 
-		/// Required designer variable.
-		/// </summary>
-		private System.ComponentModel.IContainer components = null;
-
-		/// <summary> 
-		/// Clean up any resources being used.
-		/// </summary>
-		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
-		protected override void Dispose(bool disposing)
-		{
-			if (disposing && (components != null))
-			{
-				components.Dispose();
-			}
-			base.Dispose(disposing);
-		}
-
-		#region Component Designer generated code
-
-		/// <summary> 
-		/// Required method for Designer support - do not modify 
-		/// the contents of this method with the code editor.
-		/// </summary>
-		private void InitializeComponent()
-		{
-			this.recItem = new Desktop.CommonControls.RecordField();
-			this.label2 = new System.Windows.Forms.Label();
-			this.recCharacter = new Desktop.CommonControls.RecordField();
-			this.label1 = new System.Windows.Forms.Label();
-			this.chkNot = new System.Windows.Forms.CheckBox();
-			this.SuspendLayout();
-			// 
-			// recItem
-			// 
-			this.recItem.AllowCreate = false;
-			this.recItem.Location = new System.Drawing.Point(236, 1);
-			this.recItem.Name = "recItem";
-			this.recItem.PlaceholderText = null;
-			this.recItem.Record = null;
-			this.recItem.RecordContext = null;
-			this.recItem.RecordKey = null;
-			this.recItem.RecordType = null;
-			this.recItem.Size = new System.Drawing.Size(112, 20);
-			this.recItem.TabIndex = 1;
-			this.recItem.UseAutoComplete = false;
-			// 
-			// label2
-			// 
-			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(3, 4);
-			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(56, 13);
-			this.label2.TabIndex = 5;
-			this.label2.Text = "Character:";
-			// 
-			// recCharacter
-			// 
-			this.recCharacter.AllowCreate = false;
-			this.recCharacter.Location = new System.Drawing.Point(65, 1);
-			this.recCharacter.Name = "recCharacter";
-			this.recCharacter.PlaceholderText = null;
-			this.recCharacter.Record = null;
-			this.recCharacter.RecordContext = null;
-			this.recCharacter.RecordKey = null;
-			this.recCharacter.RecordType = null;
-			this.recCharacter.Size = new System.Drawing.Size(112, 20);
-			this.recCharacter.TabIndex = 0;
-			this.recCharacter.UseAutoComplete = false;
-			// 
-			// label1
-			// 
-			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(183, 4);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(47, 13);
-			this.label1.TabIndex = 1;
-			this.label1.Text = "Has tag:";
-			// 
-			// chkNot
-			// 
-			this.chkNot.AutoSize = true;
-			this.chkNot.Location = new System.Drawing.Point(354, 3);
-			this.chkNot.Name = "chkNot";
-			this.chkNot.Size = new System.Drawing.Size(43, 17);
-			this.chkNot.TabIndex = 6;
-			this.chkNot.Text = "Not";
-			this.chkNot.UseVisualStyleBackColor = true;
-			// 
-			// CharacterTagControl
-			// 
-			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.chkNot);
-			this.Controls.Add(this.label2);
-			this.Controls.Add(this.recCharacter);
-			this.Controls.Add(this.label1);
-			this.Controls.Add(this.recItem);
-			this.Name = "CharacterTagControl";
-			this.Size = new System.Drawing.Size(521, 21);
-			this.ResumeLayout(false);
-			this.PerformLayout();
-
-		}
-
-		#endregion
-
-		private Desktop.CommonControls.RecordField recItem;
-		private System.Windows.Forms.Label label2;
-		private Desktop.CommonControls.RecordField recCharacter;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.CheckBox chkNot;
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterTagControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/CharacterTagControl.cs
deleted file mode 100644
index be3bcb5702517ccd3f08817d7c26a99d2e0fd2f8..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CharacterTagControl.cs	
+++ /dev/null
@@ -1,129 +0,0 @@
-using System.Text.RegularExpressions;
-using System.Collections.Generic;
-
-namespace SPNATI_Character_Editor
-{
-	public partial class CharacterTagControl : SubVariableControl
-	{
-		private ExpressionTest _expression;
-		
-		public CharacterTagControl()
-		{
-			InitializeComponent();
-
-			recCharacter.RecordType = typeof(Character);
-			recItem.RecordType = typeof(Tag);
-		}
-
-		protected override void OnBoundData()
-		{
-			_expression = GetValue() as ExpressionTest;
-			
-			recItem.RecordKey = null;
-
-			string pattern = @"~(.*)\.tag\.([^~]*)~";
-			Match match = Regex.Match(_expression.Expression, pattern);
-			if (match.Success)
-			{
-				string id = match.Groups[1].Value;
-				string key = match.Groups[2].Value;
-
-				Character character = CharacterDatabase.GetById(id);
-				if (character == null)
-				{
-					//default to AlsoPlaying
-					Case data = Data as Case;
-					character = CharacterDatabase.Get(data.AlsoPlaying);
-				}
-
-				recItem.RecordContext = character;
-				recCharacter.Record = character;
-
-				if (!string.IsNullOrEmpty(key) && key != "*")
-				{
-					recItem.RecordKey = key;
-				}
-			}
-			chkNot.Checked = _expression.Value == "false";
-			OnAddedToRow();
-		}
-		
-		public override void OnAddedToRow()
-		{
-			OnChangeLabel("Also Playing Tag");
-		}
-
-		protected override void AddHandlers()
-		{
-			recCharacter.RecordChanged += RecCharacter_RecordChanged;
-			recItem.RecordChanged += RecField_RecordChanged;
-			chkNot.CheckedChanged += Field_CheckedChanged;
-		}
-
-		protected override void RemoveHandlers()
-		{
-			recCharacter.RecordChanged -= RecCharacter_RecordChanged;
-			recItem.RecordChanged -= RecField_RecordChanged;
-			chkNot.CheckedChanged -= Field_CheckedChanged;
-		}
-
-		public override void ApplyMacro(List<string> values)
-		{
-			//macros should never be applied directly to a subcontrol
-		}
-
-		public override void BuildMacro(List<string> values)
-		{
-			Save();
-			values.Add(_expression.Expression);
-			values.Add(_expression.Operator);
-			values.Add(_expression.Value);
-		}
-
-		private void RecCharacter_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
-		{
-			Character character = recCharacter.Record as Character;
-			recItem.RecordContext = character;
-			recItem.RecordKey = null;
-			Save();
-		}
-
-		private void RecField_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
-		{
-			Save();	
-		}
-		private void Field_CheckedChanged(object sender, System.EventArgs e)
-		{
-			Save();
-		}
-
-		public override void Save()
-		{
-			string character = recCharacter.RecordKey;
-			string id = "_";
-			if (!string.IsNullOrEmpty(character))
-			{
-				id = CharacterDatabase.GetId(character);
-			}
-
-			string key = recItem.RecordKey;
-			if (string.IsNullOrEmpty(key))
-			{
-				key = "*";
-			}
-			string expression = $"~{id}.tag.*~".Replace("*", key);
-			_expression.Expression = expression;
-			_expression.Operator = "==";
-			if (chkNot.Checked)
-			{
-				_expression.Value = "false";
-			}
-			else
-			{
-				_expression.Value = "true";
-			}
-
-			base.Save();
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleControl.cs
deleted file mode 100644
index 316bdcc91b3e3d920f03a55d528eeda80d6c2dcb..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleControl.cs	
+++ /dev/null
@@ -1,146 +0,0 @@
-using SPNATI_Character_Editor.DataStructures;
-using System.Text.RegularExpressions;
-using System.Collections.Generic;
-
-namespace SPNATI_Character_Editor
-{
-	public partial class CollectibleControl : SubVariableControl
-	{
-		private ExpressionTest _expression;
-		private string _wildcardVariable = "~collectible.*~";
-		private TargetMode _mode = TargetMode.Self;
-
-		public CollectibleControl()
-		{
-			InitializeComponent();
-
-			recField.RecordType = typeof(Collectible);
-		}
-
-		protected override void OnBoundData()
-		{
-			_expression = GetValue() as ExpressionTest;
-
-			if (_expression.Expression.StartsWith("~collectible."))
-			{
-				_mode = TargetMode.Self;
-				recField.RecordContext = Context;
-				_wildcardVariable = "~collectible.*~";
-			}
-			else if (_expression.Expression.StartsWith("~target.collectible."))
-			{
-				_mode = TargetMode.Target;
-				SetTargetContext();
-				_wildcardVariable = "~target.collectible.*~";
-			}
-			recField.RecordKey = null;
-
-			string pattern = _wildcardVariable.Replace("*", "([^~]*)");
-			Match match = Regex.Match(_expression.Expression, pattern);
-			if (match.Success)
-			{
-				string key = match.Groups[1].Value;
-				if (!string.IsNullOrEmpty(key) && key != "*")
-				{
-					recField.RecordKey = key;
-				}
-			}
-
-			radLocked.Checked = _expression.Value == "false";
-			radUnlocked.Checked = _expression.Value != "false";
-			OnAddedToRow();
-		}
-
-		protected override void OnBindingUpdated(string property)
-		{
-			if (property == "Target" && _mode == TargetMode.Target)
-			{
-				SetTargetContext();
-			}
-		}
-
-		private void SetTargetContext()
-		{
-			Case context = Data as Case;
-			string target = context.Target;
-			if (!string.IsNullOrEmpty(target))
-			{
-				Character targetChar = CharacterDatabase.Get(target);
-				recField.RecordContext = targetChar;
-			}
-			else
-			{
-				recField.RecordContext = null;
-			}
-		}
-
-		public override void OnAddedToRow()
-		{
-			OnChangeLabel(_mode == TargetMode.Self ? "Collectible" : _mode == TargetMode.Target ? "Target Collectible" : "Also Playing Collectible");
-		}
-
-		protected override void AddHandlers()
-		{
-			recField.RecordChanged += RecField_RecordChanged;
-			radLocked.CheckedChanged += RadLocked_CheckedChanged;
-			radUnlocked.CheckedChanged += RadLocked_CheckedChanged;
-		}
-
-		protected override void RemoveHandlers()
-		{
-			recField.RecordChanged -= RecField_RecordChanged;
-			radLocked.CheckedChanged -= RadLocked_CheckedChanged;
-			radUnlocked.CheckedChanged -= RadLocked_CheckedChanged;
-		}
-
-		public override void ApplyMacro(List<string> values)
-		{
-			//macros should never be applied directly to a subcontrol
-		}
-
-		public override void BuildMacro(List<string> values)
-		{
-			Save();
-			values.Add(_expression.Expression);
-			values.Add(_expression.Operator);
-			values.Add(_expression.Value);
-		}
-
-		private void RecField_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
-		{
-			Save();	
-		}
-		private void RadLocked_CheckedChanged(object sender, System.EventArgs e)
-		{
-			Save();
-		}
-
-		public override void Save()
-		{
-			string key = recField.RecordKey;
-			if (string.IsNullOrEmpty(key))
-			{
-				key = "*";
-			}
-			string expression = _wildcardVariable.Replace("*", key);
-			_expression.Expression = expression;
-			_expression.Operator = "==";
-			if (radLocked.Checked)
-			{
-				_expression.Value = "false";
-			}
-			else
-			{
-				_expression.Value = "true";
-			}
-
-			base.Save();
-		}
-
-		private enum TargetMode
-		{
-			Self,
-			Target
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleCountControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleCountControl.cs
deleted file mode 100644
index e2476947c71f39b345470754bb6e0b55e3261d9d..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleCountControl.cs	
+++ /dev/null
@@ -1,155 +0,0 @@
-using SPNATI_Character_Editor.DataStructures;
-using System.Text.RegularExpressions;
-using System.Collections.Generic;
-using System;
-using SPNATI_Character_Editor.Providers;
-
-namespace SPNATI_Character_Editor
-{
-	public partial class CollectibleCountControl : SubVariableControl
-	{
-		private ExpressionTest _expression;
-		private string _wildcardVariable = "~collectible.*.counter~";
-		private TargetMode _mode = TargetMode.Self;
-
-		public CollectibleCountControl()
-		{
-			InitializeComponent();
-
-			recField.RecordType = typeof(Collectible);
-		}
-
-		protected override void OnBoundData()
-		{
-			_expression = GetValue() as ExpressionTest;
-
-			if (_expression.Expression.StartsWith("~collectible."))
-			{
-				_mode = TargetMode.Self;
-				recField.RecordContext = Context;
-				_wildcardVariable = "~collectible.*.counter~";
-			}
-			else if (_expression.Expression.StartsWith("~target.collectible."))
-			{
-				_mode = TargetMode.Target;
-				SetTargetContext();
-				_wildcardVariable = "~target.collectible.*.counter~";
-			}
-			recField.RecordKey = null;
-
-			string pattern = _wildcardVariable.Replace("*", "([^.~]*)");
-			Match match = Regex.Match(_expression.Expression, pattern);
-			if (match.Success)
-			{
-				string key = match.Groups[1].Value;
-				if (!string.IsNullOrEmpty(key) && key != "*")
-				{
-					recField.RecordKey = key;
-					if (recField.RecordKey == null)
-					{
-						CollectibleProvider provider = new CollectibleProvider();
-						provider.Create(key);
-						recField.RecordKey = key;
-					}
-				}
-			}
-
-			try
-			{
-				cboOperator.SelectedItem = _expression.Operator ?? "==";
-			}
-			catch
-			{
-				cboOperator.SelectedItem = "==";
-			}
-			int count;
-			int.TryParse(_expression.Value, out count);
-			valCounter.Value = Math.Max(valCounter.Minimum, Math.Min(valCounter.Maximum, count));
-			OnAddedToRow();
-		}
-
-		protected override void OnBindingUpdated(string property)
-		{
-			if (property == "Target" && _mode == TargetMode.Target)
-			{
-				SetTargetContext();
-			}
-		}
-
-		private void SetTargetContext()
-		{
-			Case context = Data as Case;
-			string target = context.Target;
-			if (!string.IsNullOrEmpty(target))
-			{
-				Character targetChar = CharacterDatabase.Get(target);
-				recField.RecordContext = targetChar;
-			}
-			else
-			{
-				recField.RecordContext = null;
-			}
-		}
-
-		public override void OnAddedToRow()
-		{
-			OnChangeLabel(_mode == TargetMode.Self ? "Collectible (Counter)" : _mode == TargetMode.Target ? "Target Collectible (Counter)" : "Other Collectible (Counter)");
-		}
-
-		protected override void AddHandlers()
-		{
-			recField.RecordChanged += RecField_RecordChanged;
-			cboOperator.SelectedIndexChanged += Field_ValueChanged;
-			valCounter.ValueChanged += Field_ValueChanged;
-		}
-
-		protected override void RemoveHandlers()
-		{
-			recField.RecordChanged -= RecField_RecordChanged;
-			cboOperator.SelectedIndexChanged -= Field_ValueChanged;
-			valCounter.ValueChanged -= Field_ValueChanged;
-		}
-
-		public override void ApplyMacro(List<string> values)
-		{
-			//macros should never be applied directly to a subcontrol
-		}
-
-		public override void BuildMacro(List<string> values)
-		{
-			Save();
-			values.Add(_expression.Expression);
-			values.Add(_expression.Operator);
-			values.Add(_expression.Value);
-		}
-
-		private void RecField_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
-		{
-			Save();	
-		}
-		private void Field_ValueChanged(object sender, System.EventArgs e)
-		{
-			Save();
-		}
-
-		public override void Save()
-		{
-			string key = recField.RecordKey;
-			if (string.IsNullOrEmpty(key))
-			{
-				key = "*";
-			}
-			string expression = _wildcardVariable.Replace("*", key);
-			_expression.Expression = expression;
-			_expression.Operator = cboOperator.Text;
-			_expression.Value = valCounter.Value.ToString();
-
-		}
-
-		private enum TargetMode
-		{
-			Self,
-			Target
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/DirectiveMarkerControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/DirectiveMarkerControl.Designer.cs
index 73c39c882cef77d873ebe01d5afa8ba669551ec0..9dbaf2778e43ae8968efde05b1d137ce884d7d03 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/DirectiveMarkerControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/DirectiveMarkerControl.Designer.cs	
@@ -28,13 +28,16 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.txtValue = new System.Windows.Forms.TextBox();
-			this.cboOperator = new System.Windows.Forms.ComboBox();
+			this.txtValue = new Desktop.Skinning.SkinnedTextBox();
+			this.cboOperator = new Desktop.Skinning.SkinnedComboBox();
 			this.recField = new Desktop.CommonControls.RecordField();
 			this.SuspendLayout();
 			// 
 			// txtValue
 			// 
+			this.txtValue.BackColor = System.Drawing.Color.White;
+			this.txtValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtValue.ForeColor = System.Drawing.Color.Black;
 			this.txtValue.Location = new System.Drawing.Point(181, 0);
 			this.txtValue.Name = "txtValue";
 			this.txtValue.Size = new System.Drawing.Size(83, 20);
@@ -42,19 +45,19 @@
 			// 
 			// cboOperator
 			// 
+			this.cboOperator.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboOperator.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboOperator.BackColor = System.Drawing.Color.White;
 			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboOperator.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboOperator.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboOperator.FormattingEnabled = true;
-			this.cboOperator.Items.AddRange(new object[] {
-            "",
-            "==",
-            "!=",
-            "<",
-            ">",
-            "<=",
-            ">="});
 			this.cboOperator.Location = new System.Drawing.Point(122, 0);
 			this.cboOperator.Name = "cboOperator";
+			this.cboOperator.SelectedIndex = -1;
+			this.cboOperator.SelectedItem = null;
 			this.cboOperator.Size = new System.Drawing.Size(56, 21);
+			this.cboOperator.Sorted = false;
 			this.cboOperator.TabIndex = 7;
 			// 
 			// recField
@@ -65,6 +68,7 @@
 			this.recField.PlaceholderText = null;
 			this.recField.Record = null;
 			this.recField.RecordContext = null;
+			this.recField.RecordFilter = null;
 			this.recField.RecordKey = null;
 			this.recField.RecordType = null;
 			this.recField.Size = new System.Drawing.Size(118, 20);
@@ -87,8 +91,8 @@
 
 		#endregion
 
-		private System.Windows.Forms.TextBox txtValue;
-		private System.Windows.Forms.ComboBox cboOperator;
+		private Desktop.Skinning.SkinnedTextBox txtValue;
+		private Desktop.Skinning.SkinnedComboBox cboOperator;
 		private Desktop.CommonControls.RecordField recField;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/DirectiveMarkerControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/DirectiveMarkerControl.cs
index b912194ea25ad4b04ee02d9a13bc968c3f9dbba4..9adab905c2c9447766889b3969c4dfc16e8e5875 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/DirectiveMarkerControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/DirectiveMarkerControl.cs	
@@ -10,6 +10,7 @@ namespace SPNATI_Character_Editor.Controls.EditControls
 		public DirectiveMarkerControl()
 		{
 			InitializeComponent();
+			cboOperator.DataSource = ExpressionTest.Operators;
 			recField.RecordType = typeof(Marker);
 		}
 
@@ -76,7 +77,7 @@ namespace SPNATI_Character_Editor.Controls.EditControls
 			txtValue.TextChanged += ValueChanged;
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			RemoveHandlers();
 			recField.RecordKey = null;
@@ -86,7 +87,7 @@ namespace SPNATI_Character_Editor.Controls.EditControls
 			Save();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			string record = recField.RecordKey;
 			if (string.IsNullOrEmpty(record))
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/ExpressionControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/ExpressionControl.Designer.cs
index df9b055f19536f48a6041ffef81b4e99de0d606d..966d630714a65e12ad65e3f8d7c7d0f84d44de9e 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/ExpressionControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/ExpressionControl.Designer.cs	
@@ -28,16 +28,20 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.cboValue = new System.Windows.Forms.ComboBox();
-			this.cboExpression = new System.Windows.Forms.ComboBox();
-			this.cboOperator = new System.Windows.Forms.ComboBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.cboValue = new Desktop.Skinning.SkinnedComboBox();
+			this.cboExpression = new Desktop.Skinning.SkinnedComboBox();
+			this.cboOperator = new Desktop.Skinning.SkinnedComboBox();
 			this.SuspendLayout();
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(3, 2);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(3, 3);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(48, 13);
 			this.label1.TabIndex = 1;
@@ -47,34 +51,51 @@
 			// 
 			this.cboValue.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
 			this.cboValue.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
+			this.cboValue.BackColor = System.Drawing.Color.White;
+			this.cboValue.DropDownStyle = System.Windows.Forms.ComboBoxStyle.Simple;
+			this.cboValue.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboValue.FormattingEnabled = true;
-			this.cboValue.Location = new System.Drawing.Point(226, 0);
+			this.cboValue.Location = new System.Drawing.Point(230, 0);
 			this.cboValue.Name = "cboValue";
+			this.cboValue.SelectedIndex = -1;
+			this.cboValue.SelectedItem = null;
 			this.cboValue.Size = new System.Drawing.Size(112, 21);
+			this.cboValue.Sorted = false;
 			this.cboValue.TabIndex = 2;
 			// 
 			// cboExpression
 			// 
+			this.cboExpression.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboExpression.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboExpression.BackColor = System.Drawing.Color.White;
+			this.cboExpression.DropDownStyle = System.Windows.Forms.ComboBoxStyle.Simple;
+			this.cboExpression.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboExpression.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboExpression.FormattingEnabled = true;
 			this.cboExpression.Location = new System.Drawing.Point(57, 0);
 			this.cboExpression.Name = "cboExpression";
+			this.cboExpression.SelectedIndex = -1;
+			this.cboExpression.SelectedItem = null;
 			this.cboExpression.Size = new System.Drawing.Size(118, 21);
+			this.cboExpression.Sorted = false;
 			this.cboExpression.TabIndex = 0;
 			// 
 			// cboOperator
 			// 
+			this.cboOperator.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboOperator.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboOperator.BackColor = System.Drawing.Color.White;
 			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboOperator.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboOperator.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboOperator.FormattingEnabled = true;
-			this.cboOperator.Items.AddRange(new object[] {
-            "==",
-            "<=",
-            "<",
-            ">",
-            ">=",
-            "!="});
 			this.cboOperator.Location = new System.Drawing.Point(181, 0);
 			this.cboOperator.Name = "cboOperator";
-			this.cboOperator.Size = new System.Drawing.Size(39, 21);
+			this.cboOperator.SelectedIndex = -1;
+			this.cboOperator.SelectedItem = null;
+			this.cboOperator.Size = new System.Drawing.Size(43, 21);
+			this.cboOperator.Sorted = false;
 			this.cboOperator.TabIndex = 1;
 			// 
 			// ExpressionControl
@@ -93,9 +114,9 @@
 		}
 
 		#endregion
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.ComboBox cboValue;
-		private System.Windows.Forms.ComboBox cboExpression;
-		private System.Windows.Forms.ComboBox cboOperator;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedComboBox cboValue;
+		private Desktop.Skinning.SkinnedComboBox cboExpression;
+		private Desktop.Skinning.SkinnedComboBox cboOperator;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/ExpressionControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/ExpressionControl.cs
index 3044e406b8932ead593549ffca696be378388784..4797b285f4e5ebc427abef3e48003bc73b4ebece 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/ExpressionControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/ExpressionControl.cs	
@@ -4,11 +4,14 @@ using System;
 using System.Text.RegularExpressions;
 using System.Windows.Forms;
 using System.Collections.Generic;
+using System.Reflection;
 
 namespace SPNATI_Character_Editor
 {
 	public partial class ExpressionControl : PropertyEditControl
 	{
+		private static Dictionary<List<string>, Type> _subControlTypes;
+
 		private ExpressionTest _expression;
 		private string _currentVariable;
 
@@ -17,10 +20,11 @@ namespace SPNATI_Character_Editor
 		public ExpressionControl()
 		{
 			InitializeComponent();
-
+			cboOperator.DataSource = ExpressionTest.Operators;
 			cboExpression.Items.AddRange(new string[] {
 				"~background~",
 				"~background.location~",
+				"~background.time~",
 				"~cards~",
 				"~clothing~",
 				"~clothing.plural~",
@@ -38,6 +42,26 @@ namespace SPNATI_Character_Editor
 			});
 			cboExpression.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
 			cboExpression.AutoCompleteSource = AutoCompleteSource.ListItems;
+
+			if (_subControlTypes == null)
+			{
+				_subControlTypes = new Dictionary<List<string>, Type>();
+				foreach (Type type in this.GetType().Assembly.GetTypes())
+				{
+					foreach (SubVariableAttribute attrib in type.GetCustomAttributes<SubVariableAttribute>())
+					{
+						_subControlTypes[attrib.Variables] = type;
+					}
+				}
+			}
+		}
+
+		protected override void OnIndexChanged()
+		{
+			if (_subcontrol != null)
+			{
+				_subcontrol.Index = Index;
+			}
 		}
 
 		public override void ApplyMacro(List<string> values)
@@ -169,66 +193,36 @@ namespace SPNATI_Character_Editor
 			DestroySubControl();
 
 			string variable = expression.Expression;
-			if (!string.IsNullOrEmpty(variable))
+			if (!string.IsNullOrEmpty(variable) && !variable.StartsWith("~clothing"))
 			{
-				if (variable.StartsWith("~collectible."))
-				{
-					if (variable.EndsWith(".counter~"))
-					{
-						//self collectible counter check
-						_subcontrol = new CollectibleCountControl();
-					}
-					else
-					{
-						//self collectible unlock check
-						_subcontrol = new CollectibleControl();
-					}
-				}
-				else if (variable.StartsWith("~target.collectible."))
+				Type bestMatch = null;
+				int bestCount = 0;
+				foreach (KeyValuePair<List<string>, Type> kvp in _subControlTypes)
 				{
-					if (variable.EndsWith(".counter~"))
+					for (int i = 0; i < kvp.Key.Count; i++)
 					{
-						//target collectible counter check
-						_subcontrol = new CollectibleCountControl();
-					}
-					else
-					{
-						//target collectible unlock check
-						_subcontrol = new CollectibleControl();
-					}
-				}
-				else if (variable.Contains(".collectible."))
-				{
-					if (variable.Contains(".counter~"))
-					{
-						//also playing collectible counter check
-						_subcontrol = new CharacterCollectibleCountControl();
-					}
-					else
-					{
-						//also playing collectible unlock check
-						_subcontrol = new CharacterCollectibleControl();
+						string variablePiece = kvp.Key[i];
+						if (variable.StartsWith("~" + variablePiece + "~") || variable.StartsWith("~" + variablePiece + "."))
+						{
+							variable = _expression.Expression = _expression.Expression.Insert(1, "self.");
+						}
+						if (variable.Contains("." + variablePiece))
+						{
+							if (i == kvp.Key.Count - 1 && bestCount < kvp.Key.Count)
+							{
+								bestCount = kvp.Key.Count;
+								bestMatch = kvp.Value;
+							}
+						}
+						else
+						{
+							break;
+						}
 					}
 				}
-				else if (variable.StartsWith("~self.tag.") || variable.StartsWith("~target.tag."))
-				{
-					_subcontrol = new TagControl();
-				}
-				else if (variable.Contains(".tag."))
-				{
-					_subcontrol = new CharacterTagControl();
-				}
-				else if (variable.StartsWith("~persistent"))
+				if (bestMatch != null)
 				{
-					_subcontrol = new PersistentMarkerControl();
-				}
-				else if (variable.StartsWith("~target.persistent."))
-				{
-					_subcontrol = new PersistentMarkerControl();
-				}
-				else if (variable.Contains(".persistent."))
-				{
-					_subcontrol = new CharacterPersistentMarkerControl();
+					_subcontrol = Activator.CreateInstance(bestMatch) as SubVariableControl;
 				}
 
 				if (_subcontrol != null)
@@ -238,12 +232,13 @@ namespace SPNATI_Character_Editor
 						ctl.Visible = false;
 					}
 
+					_subcontrol.ParentControl = this;
 					_subcontrol.Margin = new Padding(0);
 					_subcontrol.Left = 0;
 					_subcontrol.Top = 0;
 					_subcontrol.Dock = DockStyle.Fill;
 					_subcontrol.ChangeLabel += _subcontrol_ChangeLabel;
-					_subcontrol.SetData(Data, Property, Index, Context, UndoManager, PreviewData);
+					_subcontrol.SetData(Data, Property, Index, Context, UndoManager, PreviewData, ParentTable);
 					_subcontrol.RemoveEventHandlers(); //these'll be added back when this controls AddHandlers gets called, so this is a method to prevent double handlers
 					Controls.Add(_subcontrol);
 				}
@@ -300,7 +295,7 @@ namespace SPNATI_Character_Editor
 			Save();
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			if (_subcontrol != null)
 			{
@@ -315,7 +310,7 @@ namespace SPNATI_Character_Editor
 			Save();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			if (_subcontrol != null)
 			{
@@ -367,6 +362,9 @@ namespace SPNATI_Character_Editor
 						"5",
 					});
 					break;
+				case "~clothing~":
+					cboValue.Items.AddRange(ClothingDatabase.Items.Values);
+					break;
 				case "~clothing.position~":
 					cboValue.Items.AddRange(new string[] {
 						"upper",
@@ -402,6 +400,12 @@ namespace SPNATI_Character_Editor
 						"outdoors",
 					});
 					break;
+				case "~background.time~":
+					cboValue.Items.AddRange(new string[] {
+						"day",
+						"night",
+					});
+					break;
 				case "~background~":
 					cboValue.Items.AddRange(new string[] {
 						"inventory",
@@ -576,6 +580,8 @@ namespace SPNATI_Character_Editor
 
 	public class SubVariableControl : PropertyEditControl
 	{
+		public PropertyEditControl ParentControl;
+
 		public void UpdateBinding(string property)
 		{
 			OnBindingUpdated(property);
@@ -590,5 +596,73 @@ namespace SPNATI_Character_Editor
 		{
 			RemoveHandlers();
 		}
+
+		public override bool IsUpdating
+		{
+			get
+			{
+				return base.IsUpdating || ParentControl.IsUpdating;
+			}
+		}
+
+		public override void BuildMacro(List<string> values)
+		{
+			Save();
+			ExpressionTest expression = GetValue() as ExpressionTest;
+			values.Add(expression.Expression);
+			values.Add(expression.Operator);
+			values.Add(expression.Value);
+		}
+
+		protected void ExtractExpressionPieces(out string targetType, out string variable)
+		{
+			ExpressionTest expression = GetValue() as ExpressionTest;
+			string expr = expression.Expression?.ToLower() ?? "";
+			expr = expr.Trim('~');
+			int period = expr.IndexOf('.');
+			targetType = "";
+			if (period >= 0)
+			{
+				targetType = expr.Substring(0, period);
+				if (expr.Length > period + 1)
+				{
+					expr = expr.Substring(period + 1);
+				}
+			}
+			else
+			{
+				targetType = expr;
+			}
+			if (targetType == "_")
+			{
+				//default to AlsoPlaying
+				Case data = Data as Case;
+				Character character = CharacterDatabase.Get(data.AlsoPlaying);
+				if (character != null)
+				{
+					string id = CharacterDatabase.GetId(character);
+					targetType = id;
+				}
+			}
+
+			variable = expr;
+		}
+	}
+
+	/// <summary>
+	/// Maps a control to a particular variable expression. If multiple variable pieces are supplied, then it's expected for all to appear in the same
+	/// express (ex. collectible + counter for an expression of ~x.collectible.y.counter~)
+	/// To map multiple distinct variables to the same control, use multiple of this atttribute
+	/// </summary>
+	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+	public class SubVariableAttribute : Attribute
+	{
+		public List<string> Variables { get; set; }
+
+		public SubVariableAttribute(params string[] variablePieces)
+		{
+			Variables = new List<string>();
+			Variables.AddRange(variablePieces);
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.Designer.cs
index 2e61e8700b1d00fd7f0d3028abcd94cc1c6f798d..ef2841f260d9a16b1d67d66a6c89deb0f14efd3d 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.Designer.cs	
@@ -29,14 +29,14 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			this.label1 = new System.Windows.Forms.Label();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
 			this.recTag = new Desktop.CommonControls.RecordField();
-			this.label2 = new System.Windows.Forms.Label();
-			this.cboGender = new System.Windows.Forms.ComboBox();
-			this.valFrom = new System.Windows.Forms.NumericUpDown();
-			this.label3 = new System.Windows.Forms.Label();
-			this.valTo = new System.Windows.Forms.NumericUpDown();
-			this.cmdExpand = new System.Windows.Forms.Button();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.cboGender = new Desktop.Skinning.SkinnedComboBox();
+			this.valFrom = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.valTo = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.cmdExpand = new Desktop.Skinning.SkinnedIcon();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
 			this.tableAdvanced = new Desktop.CommonControls.PropertyTable();
 			((System.ComponentModel.ISupportInitialize)(this.valFrom)).BeginInit();
@@ -46,6 +46,8 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(84, 2);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(41, 13);
@@ -60,6 +62,7 @@
 			this.recTag.PlaceholderText = null;
 			this.recTag.Record = null;
 			this.recTag.RecordContext = null;
+			this.recTag.RecordFilter = null;
 			this.recTag.RecordKey = null;
 			this.recTag.RecordType = null;
 			this.recTag.Size = new System.Drawing.Size(98, 20);
@@ -69,6 +72,10 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label2.Location = new System.Drawing.Point(230, 2);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(45, 13);
@@ -77,19 +84,26 @@
 			// 
 			// cboGender
 			// 
+			this.cboGender.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboGender.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboGender.BackColor = System.Drawing.Color.White;
 			this.cboGender.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboGender.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboGender.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboGender.FormattingEnabled = true;
-			this.cboGender.Items.AddRange(new object[] {
-            "",
-            "female",
-            "male"});
 			this.cboGender.Location = new System.Drawing.Point(281, 0);
 			this.cboGender.Name = "cboGender";
+			this.cboGender.SelectedIndex = -1;
+			this.cboGender.SelectedItem = null;
 			this.cboGender.Size = new System.Drawing.Size(62, 21);
+			this.cboGender.Sorted = false;
 			this.cboGender.TabIndex = 3;
 			// 
 			// valFrom
 			// 
+			this.valFrom.BackColor = System.Drawing.Color.White;
+			this.valFrom.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valFrom.ForeColor = System.Drawing.Color.Black;
 			this.valFrom.Location = new System.Drawing.Point(3, 0);
 			this.valFrom.Maximum = new decimal(new int[] {
             5,
@@ -103,6 +117,8 @@
 			// label3
 			// 
 			this.label3.AutoSize = true;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label3.Location = new System.Drawing.Point(33, 2);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(19, 13);
@@ -111,6 +127,9 @@
 			// 
 			// valTo
 			// 
+			this.valTo.BackColor = System.Drawing.Color.White;
+			this.valTo.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valTo.ForeColor = System.Drawing.Color.Black;
 			this.valTo.Location = new System.Drawing.Point(48, 0);
 			this.valTo.Maximum = new decimal(new int[] {
             5,
@@ -124,6 +143,9 @@
 			// cmdExpand
 			// 
 			this.cmdExpand.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdExpand.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdExpand.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdExpand.Flat = false;
 			this.cmdExpand.FlatAppearance.BorderSize = 0;
 			this.cmdExpand.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
 			this.cmdExpand.Image = global::SPNATI_Character_Editor.Properties.Resources.ChevronDown;
@@ -138,19 +160,23 @@
 			// 
 			// tableAdvanced
 			// 
-			this.tableAdvanced.AllowDelete = false;
+			this.tableAdvanced.AllowDelete = true;
 			this.tableAdvanced.AllowFavorites = false;
 			this.tableAdvanced.AllowHelp = false;
 			this.tableAdvanced.AllowMacros = false;
 			this.tableAdvanced.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.tableAdvanced.BackColor = System.Drawing.Color.Transparent;
 			this.tableAdvanced.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
 			this.tableAdvanced.Data = null;
+			this.tableAdvanced.HeaderType = Desktop.Skinning.SkinnedBackgroundType.Secondary;
 			this.tableAdvanced.HideAddField = true;
-			this.tableAdvanced.HideSpeedButtons = true;
+			this.tableAdvanced.HideSpeedButtons = false;
 			this.tableAdvanced.Location = new System.Drawing.Point(3, 27);
+			this.tableAdvanced.ModifyingProperty = null;
 			this.tableAdvanced.Name = "tableAdvanced";
+			this.tableAdvanced.PanelType = Desktop.Skinning.SkinnedBackgroundType.Transparent;
 			this.tableAdvanced.PlaceholderText = null;
 			this.tableAdvanced.PreserveControls = true;
 			this.tableAdvanced.PreviewData = null;
@@ -161,7 +187,7 @@
 			this.tableAdvanced.Sorted = true;
 			this.tableAdvanced.TabIndex = 10;
 			this.tableAdvanced.UndoManager = null;
-			this.tableAdvanced.UseAutoComplete = false;
+			this.tableAdvanced.UseAutoComplete = true;
 			// 
 			// FilterControl
 			// 
@@ -187,14 +213,14 @@
 
 		#endregion
 
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedLabel label1;
 		private Desktop.CommonControls.RecordField recTag;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.ComboBox cboGender;
-		private System.Windows.Forms.NumericUpDown valFrom;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.NumericUpDown valTo;
-		private System.Windows.Forms.Button cmdExpand;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedComboBox cboGender;
+		private Desktop.Skinning.SkinnedNumericUpDown valFrom;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedNumericUpDown valTo;
+		private Desktop.Skinning.SkinnedIcon cmdExpand;
 		private System.Windows.Forms.ToolTip toolTip1;
 		private Desktop.CommonControls.PropertyTable tableAdvanced;
 	}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.cs
index d4eef7b3588a791bbcb1071c4689be8d06e4ba83..29918c842708de9ee2a738478bf7c79e65933e19 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.cs	
@@ -15,7 +15,7 @@ namespace SPNATI_Character_Editor
 		public FilterControl()
 		{
 			InitializeComponent();
-
+			cboGender.Items.AddRange(new string[] { "", "female", "male" });
 			recTag.RecordType = typeof(Tag);
 		}
 
@@ -44,8 +44,18 @@ namespace SPNATI_Character_Editor
 					_filter.FilterId = values[6];
 					_filter.FilterStage = values[7];
 				}
-
-				RebindTable();
+				if (values.Count > 16)
+				{
+					_filter.Hand = values[8];
+					_filter.Layers = values[9];
+					_filter.StartingLayers = values[10];
+					_filter.SaidMarker = values[11];
+					_filter.NotSaidMarker = values[12];
+					_filter.SayingMarker = values[13];
+					_filter.Saying = values[14];
+					_filter.TimeInStage = values[15];
+					_filter.ConsecutiveLosses = values[16];
+				}
 
 				ToggleCollapsed(!_filter.HasAdvancedConditions);
 			}
@@ -64,6 +74,15 @@ namespace SPNATI_Character_Editor
 			values.Add(_filter.Variable);
 			values.Add(_filter.FilterId);
 			values.Add(_filter.FilterStage);
+			values.Add(_filter.Hand);
+			values.Add(_filter.Layers);
+			values.Add(_filter.StartingLayers);
+			values.Add(_filter.SaidMarker);
+			values.Add(_filter.NotSaidMarker);
+			values.Add(_filter.SayingMarker);
+			values.Add(_filter.Saying);
+			values.Add(_filter.TimeInStage);
+			values.Add(_filter.ConsecutiveLosses);
 		}
 
 		protected override void OnBoundData()
@@ -175,7 +194,7 @@ namespace SPNATI_Character_Editor
 			return GUIHelper.ToRange(from, to);
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			RemoveHandlers();
 			valFrom.Text = "";
@@ -184,24 +203,11 @@ namespace SPNATI_Character_Editor
 			recTag.RecordKey = null;
 			_filter.Status = null;
 
-			RebindTable();
-
 			Save();
 			AddHandlers();
 		}
 
-		private void RebindTable()
-		{
-			//TODO: Once properties serialize properly with SpnatiXmlSerializer, we can switch TargetCondition to use a BindableObject, make
-			//the fields properties, and get rid of this method
-			tableAdvanced.UpdateProperty("Status");
-			tableAdvanced.UpdateProperty("Role");
-			tableAdvanced.UpdateProperty("Variable");
-			tableAdvanced.UpdateProperty("FilterId");
-			tableAdvanced.UpdateProperty("FilterStage");
-		}
-
-		public override void Save()
+		protected override void OnSave()
 		{
 			string count = GetCount() ?? "0";
 			string tag = recTag.RecordKey;
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.resx b/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.resx
index d54ad1e479b4a073f72aa823633b981956005d39..df8339b688f963f4372fdb280568769b960537d6 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.resx	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/FilterControl.resx	
@@ -120,7 +120,4 @@
   <metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>17, 17</value>
   </metadata>
-  <metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>17, 17</value>
-  </metadata>
 </root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/HorizontalAlignmentControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/HorizontalAlignmentControl.Designer.cs
index 7edfca052f1691c654f3462c5303e1987b08ecfa..e72e94f36e9be8aba3bd0e055fc311e526fc6024 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/HorizontalAlignmentControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/HorizontalAlignmentControl.Designer.cs	
@@ -29,9 +29,9 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			this.chkRight = new System.Windows.Forms.RadioButton();
-			this.chkMiddle = new System.Windows.Forms.RadioButton();
-			this.chkLeft = new System.Windows.Forms.RadioButton();
+			this.chkRight = new Desktop.Skinning.SkinnedRadioButton();
+			this.chkMiddle = new Desktop.Skinning.SkinnedRadioButton();
+			this.chkLeft = new Desktop.Skinning.SkinnedRadioButton();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
 			this.SuspendLayout();
 			// 
@@ -86,9 +86,9 @@
 		}
 
 		#endregion
-		private System.Windows.Forms.RadioButton chkLeft;
-		private System.Windows.Forms.RadioButton chkMiddle;
-		private System.Windows.Forms.RadioButton chkRight;
+		private Desktop.Skinning.SkinnedRadioButton chkLeft;
+		private Desktop.Skinning.SkinnedRadioButton chkMiddle;
+		private Desktop.Skinning.SkinnedRadioButton chkRight;
 		private System.Windows.Forms.ToolTip toolTip1;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/HorizontalAlignmentControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/HorizontalAlignmentControl.cs
index 009f841190f77bc1d1033e409a8f497298a85db7..cd477b0b0eede7d3a9c6ab1c07767d27054e2102 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/HorizontalAlignmentControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/HorizontalAlignmentControl.cs	
@@ -47,7 +47,7 @@ namespace SPNATI_Character_Editor.Controls.EditControls
 			Save();
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			RemoveHandlers();
 			chkLeft.Checked = false;
@@ -56,7 +56,7 @@ namespace SPNATI_Character_Editor.Controls.EditControls
 			AddHandlers();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			if (chkLeft.Checked)
 			{
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/ImageFileSelectControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/ImageFileSelectControl.Designer.cs
index 168740f1686d58588ca7f6aafa22e7994bc24b0a..4933922c58e90f04f5262745cea1b7fd7214794f 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/ImageFileSelectControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/ImageFileSelectControl.Designer.cs	
@@ -29,15 +29,19 @@
 		private void InitializeComponent()
 		{
 			this.txtValue = new Desktop.CommonControls.TextField();
-			this.cmdBrowse = new System.Windows.Forms.Button();
+			this.cmdBrowse = new Desktop.Skinning.SkinnedButton();
 			this.openFileDialog1 = new SPNATI_Character_Editor.Controls.CharacterImageDialog();
 			this.SuspendLayout();
 			// 
 			// txtValue
 			// 
 			this.txtValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtValue.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.txtValue.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
 			this.txtValue.Location = new System.Drawing.Point(0, 0);
+			this.txtValue.Multiline = false;
 			this.txtValue.Name = "txtValue";
+			this.txtValue.PlaceholderText = "";
 			this.txtValue.ReadOnly = true;
 			this.txtValue.Size = new System.Drawing.Size(318, 20);
 			this.txtValue.TabIndex = 0;
@@ -45,6 +49,10 @@
 			// cmdBrowse
 			// 
 			this.cmdBrowse.Anchor = System.Windows.Forms.AnchorStyles.Right;
+			this.cmdBrowse.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdBrowse.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cmdBrowse.Flat = false;
+			this.cmdBrowse.ForeColor = System.Drawing.Color.Blue;
 			this.cmdBrowse.Location = new System.Drawing.Point(324, -1);
 			this.cmdBrowse.Name = "cmdBrowse";
 			this.cmdBrowse.Size = new System.Drawing.Size(27, 22);
@@ -56,6 +64,8 @@
 			// openFileDialog1
 			// 
 			this.openFileDialog1.Filter = "";
+			this.openFileDialog1.IncludeOpponents = false;
+			this.openFileDialog1.UseAbsolutePaths = false;
 			// 
 			// ImageFileSelectControl
 			// 
@@ -66,14 +76,13 @@
 			this.Name = "ImageFileSelectControl";
 			this.Size = new System.Drawing.Size(351, 20);
 			this.ResumeLayout(false);
-			this.PerformLayout();
 
 		}
 
 		#endregion
 
 		private Desktop.CommonControls.TextField txtValue;
-		private System.Windows.Forms.Button cmdBrowse;
+		private Desktop.Skinning.SkinnedButton cmdBrowse;
 		private CharacterImageDialog openFileDialog1;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/ImageFileSelectControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/ImageFileSelectControl.cs
index 7bfb508baa8b18a73278dedb3200fe94ec9d00c2..8affccf6ec10bf9f504a69ac8822debd7307f26d 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/ImageFileSelectControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/ImageFileSelectControl.cs	
@@ -52,13 +52,13 @@ namespace SPNATI_Character_Editor.Controls
 			}
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			txtValue.Text = "";
 			Save();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			if (txtValue.Text == "")
 			{
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/LayerDifferenceControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/LayerDifferenceControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8a7a6b265170a6021ca11476d171251d279e26bf
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/LayerDifferenceControl.Designer.cs	
@@ -0,0 +1,148 @@
+namespace SPNATI_Character_Editor.Controls.EditControls
+{
+	partial class LayerDifferenceControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.recType = new Desktop.CommonControls.RecordField();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.recCharacter = new Desktop.CommonControls.RecordField();
+			this.txtValue = new Desktop.Skinning.SkinnedTextBox();
+			this.cboOperator = new Desktop.Skinning.SkinnedComboBox();
+			this.SuspendLayout();
+			// 
+			// label2
+			// 
+			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label2.Location = new System.Drawing.Point(4, 3);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(39, 13);
+			this.label2.TabIndex = 12;
+			this.label2.Text = "Player:";
+			// 
+			// recType
+			// 
+			this.recType.AllowCreate = false;
+			this.recType.Location = new System.Drawing.Point(49, 0);
+			this.recType.Name = "recType";
+			this.recType.PlaceholderText = null;
+			this.recType.Record = null;
+			this.recType.RecordContext = null;
+			this.recType.RecordFilter = null;
+			this.recType.RecordKey = null;
+			this.recType.RecordType = null;
+			this.recType.Size = new System.Drawing.Size(112, 20);
+			this.recType.TabIndex = 11;
+			this.recType.UseAutoComplete = false;
+			// 
+			// label1
+			// 
+			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(167, 3);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(73, 13);
+			this.label1.TabIndex = 13;
+			this.label1.Text = "compare with:";
+			// 
+			// recCharacter
+			// 
+			this.recCharacter.AllowCreate = false;
+			this.recCharacter.Location = new System.Drawing.Point(246, 0);
+			this.recCharacter.Name = "recCharacter";
+			this.recCharacter.PlaceholderText = null;
+			this.recCharacter.Record = null;
+			this.recCharacter.RecordContext = null;
+			this.recCharacter.RecordFilter = null;
+			this.recCharacter.RecordKey = null;
+			this.recCharacter.RecordType = null;
+			this.recCharacter.Size = new System.Drawing.Size(112, 20);
+			this.recCharacter.TabIndex = 14;
+			this.recCharacter.UseAutoComplete = false;
+			// 
+			// txtValue
+			// 
+			this.txtValue.BackColor = System.Drawing.Color.White;
+			this.txtValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtValue.ForeColor = System.Drawing.Color.Black;
+			this.txtValue.Location = new System.Drawing.Point(421, 0);
+			this.txtValue.Name = "txtValue";
+			this.txtValue.Size = new System.Drawing.Size(100, 20);
+			this.txtValue.TabIndex = 16;
+			// 
+			// cboOperator
+			// 
+			this.cboOperator.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboOperator.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboOperator.BackColor = System.Drawing.Color.White;
+			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboOperator.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboOperator.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboOperator.FormattingEnabled = true;
+			this.cboOperator.Location = new System.Drawing.Point(361, 0);
+			this.cboOperator.Name = "cboOperator";
+			this.cboOperator.SelectedIndex = -1;
+			this.cboOperator.SelectedItem = null;
+			this.cboOperator.Size = new System.Drawing.Size(56, 21);
+			this.cboOperator.Sorted = false;
+			this.cboOperator.TabIndex = 15;
+			// 
+			// LayerDifferenceControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.txtValue);
+			this.Controls.Add(this.cboOperator);
+			this.Controls.Add(this.recCharacter);
+			this.Controls.Add(this.label1);
+			this.Controls.Add(this.label2);
+			this.Controls.Add(this.recType);
+			this.Name = "LayerDifferenceControl";
+			this.Size = new System.Drawing.Size(618, 21);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.CommonControls.RecordField recType;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.CommonControls.RecordField recCharacter;
+		private Desktop.Skinning.SkinnedTextBox txtValue;
+		private Desktop.Skinning.SkinnedComboBox cboOperator;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/LayerDifferenceControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/LayerDifferenceControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..48c8df46543c0b3b6730dfb51b78573eb67dcab1
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/LayerDifferenceControl.cs	
@@ -0,0 +1,109 @@
+using System;
+
+namespace SPNATI_Character_Editor.Controls.EditControls
+{
+	[SubVariable("diff")]
+	public partial class LayerDifferenceControl : SubVariableControl
+	{
+		private ExpressionTest _expression;
+
+		public LayerDifferenceControl()
+		{
+			InitializeComponent();
+			cboOperator.DataSource = ExpressionTest.Operators;
+			recType.RecordType = typeof(TargetId);
+			recCharacter.RecordType = typeof(TargetId);
+
+			Bindings.Add("AlsoPlaying");
+		}
+
+		protected override void OnBoundData()
+		{
+			_expression = GetValue() as ExpressionTest;
+			string targetType;
+			string variable;
+			ExtractExpressionPieces(out targetType, out variable);
+
+			recType.RecordKey = targetType;
+			variable = variable.TrimEnd(')');
+			int start = variable.IndexOf('(');
+			if (start >= 0 && start < variable.Length - 1)
+			{
+				string id = variable.Substring(start + 1);
+				recCharacter.RecordKey = id;
+				//Character character = CharacterDatabase.GetById(id);
+				//recCharacter.Record = character;
+			}
+
+			cboOperator.Text = _expression.Operator ?? "==";
+			txtValue.Text = _expression.Value;
+			OnAddedToRow();
+		}
+
+		public override void OnAddedToRow()
+		{
+			OnChangeLabel("Layer Difference");
+		}
+
+		protected override void OnBindingUpdated(string property)
+		{
+			if (property == "AlsoPlaying" && (recType.RecordKey == "_" || recType.RecordKey == null))
+			{
+				string targetType;
+				string character;
+				ExtractExpressionPieces(out targetType, out character);
+				recType.RecordKey = targetType;
+			}
+		}
+
+		protected override void AddHandlers()
+		{
+			recType.RecordChanged += RecType_RecordChanged;
+			cboOperator.SelectedIndexChanged += ValueChanged;
+			recCharacter.RecordChanged += RecType_RecordChanged;
+			txtValue.TextChanged += ValueChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			recType.RecordChanged -= RecType_RecordChanged;
+			cboOperator.SelectedIndexChanged -= ValueChanged;
+			recCharacter.RecordChanged -= RecType_RecordChanged;
+			txtValue.TextChanged -= ValueChanged;
+		}
+
+		private void RecType_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
+		{
+			Save();
+		}
+		private void ValueChanged(object sender, EventArgs e)
+		{
+			Save();
+		}
+
+		protected override void OnSave()
+		{
+			string targetType = recType.RecordKey;
+			if (string.IsNullOrEmpty(targetType))
+			{
+				targetType = "_";
+			}
+			string id = recCharacter.RecordKey ?? "";
+			_expression.Expression = $"~{targetType}.diff({id})~";
+
+			string op = cboOperator.SelectedItem?.ToString();
+			if (string.IsNullOrEmpty(op))
+			{
+				op = "==";
+			}
+			_expression.Operator = op;
+
+			string value = txtValue.Text;
+			if (string.IsNullOrEmpty(value))
+			{
+				value = null;
+			}
+			_expression.Value = value;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleCountControl.resx b/editor source/SPNATI Character Editor/Controls/EditControls/LayerDifferenceControl.resx
similarity index 100%
rename from editor source/SPNATI Character Editor/Controls/EditControls/CollectibleCountControl.resx
rename to editor source/SPNATI Character Editor/Controls/EditControls/LayerDifferenceControl.resx
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/MarkerConditionControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/MarkerConditionControl.Designer.cs
index 3c3cdec2456140c136397f095bda548cdfcfd6b8..7ed4a2ecd779cf3461121ed9804513c39377eff7 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/MarkerConditionControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/MarkerConditionControl.Designer.cs	
@@ -29,9 +29,9 @@
 		private void InitializeComponent()
 		{
 			this.recField = new Desktop.CommonControls.RecordField();
-			this.chkPerTarget = new System.Windows.Forms.CheckBox();
-			this.txtValue = new System.Windows.Forms.TextBox();
-			this.cboOperator = new System.Windows.Forms.ComboBox();
+			this.chkPerTarget = new Desktop.Skinning.SkinnedCheckBox();
+			this.txtValue = new Desktop.Skinning.SkinnedTextBox();
+			this.cboOperator = new Desktop.Skinning.SkinnedComboBox();
 			this.SuspendLayout();
 			// 
 			// recField
@@ -42,6 +42,7 @@
 			this.recField.PlaceholderText = null;
 			this.recField.Record = null;
 			this.recField.RecordContext = null;
+			this.recField.RecordFilter = null;
 			this.recField.RecordKey = null;
 			this.recField.RecordType = null;
 			this.recField.Size = new System.Drawing.Size(150, 20);
@@ -60,6 +61,9 @@
 			// 
 			// txtValue
 			// 
+			this.txtValue.BackColor = System.Drawing.Color.White;
+			this.txtValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtValue.ForeColor = System.Drawing.Color.Black;
 			this.txtValue.Location = new System.Drawing.Point(218, 0);
 			this.txtValue.Name = "txtValue";
 			this.txtValue.Size = new System.Drawing.Size(100, 20);
@@ -67,22 +71,22 @@
 			// 
 			// cboOperator
 			// 
+			this.cboOperator.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboOperator.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboOperator.BackColor = System.Drawing.Color.White;
 			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboOperator.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboOperator.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboOperator.FormattingEnabled = true;
-			this.cboOperator.Items.AddRange(new object[] {
-            "",
-            "==",
-            "!=",
-            "<",
-            ">",
-            "<=",
-            ">="});
 			this.cboOperator.Location = new System.Drawing.Point(156, 0);
 			this.cboOperator.Name = "cboOperator";
+			this.cboOperator.SelectedIndex = -1;
+			this.cboOperator.SelectedItem = null;
 			this.cboOperator.Size = new System.Drawing.Size(56, 21);
+			this.cboOperator.Sorted = false;
 			this.cboOperator.TabIndex = 4;
 			// 
-			// MarkerControl
+			// MarkerConditionControl
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
@@ -90,7 +94,7 @@
 			this.Controls.Add(this.txtValue);
 			this.Controls.Add(this.cboOperator);
 			this.Controls.Add(this.recField);
-			this.Name = "MarkerControl";
+			this.Name = "MarkerConditionControl";
 			this.Size = new System.Drawing.Size(605, 21);
 			this.ResumeLayout(false);
 			this.PerformLayout();
@@ -100,8 +104,8 @@
 		#endregion
 
 		private Desktop.CommonControls.RecordField recField;
-		private System.Windows.Forms.CheckBox chkPerTarget;
-		private System.Windows.Forms.TextBox txtValue;
-		private System.Windows.Forms.ComboBox cboOperator;
+		private Desktop.Skinning.SkinnedCheckBox chkPerTarget;
+		private Desktop.Skinning.SkinnedTextBox txtValue;
+		private Desktop.Skinning.SkinnedComboBox cboOperator;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/MarkerConditionControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/MarkerConditionControl.cs
index d1a857d1b9151251d2780dc629d1ca08e39201c6..cd0f8e01d49270998a449bfca0ae367e1e62c6a7 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/MarkerConditionControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/MarkerConditionControl.cs	
@@ -11,7 +11,7 @@ namespace SPNATI_Character_Editor
 		public MarkerConditionControl()
 		{
 			InitializeComponent();
-
+			cboOperator.DataSource = ExpressionTest.Operators;
 			recField.RecordType = typeof(Marker);
 		}
 
@@ -120,7 +120,7 @@ namespace SPNATI_Character_Editor
 			chkPerTarget.CheckedChanged += ValueChanged;
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			RemoveHandlers();
 			recField.RecordKey = null;
@@ -153,7 +153,7 @@ namespace SPNATI_Character_Editor
 			return record;
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			string value = BuildValue();
 			SetValue(value);
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/MarkerControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/MarkerControl.cs
index c09e6d348cc6ba53419daeef752dc6d0777f9283..908bb24dea37db1744ef01cdc1bcfb1337636fdc 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/MarkerControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/MarkerControl.cs	
@@ -77,12 +77,12 @@ namespace SPNATI_Character_Editor
 			Save();
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			recField.RecordKey = null;
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			string key = recField.RecordKey;
 			SetValue(key);
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/MeasurementControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/MeasurementControl.Designer.cs
index 53b0a77a9ed6ed6eea1e1ae57ec21820275fc448..26502b41dc3975753aef46a69252239faa48549c 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/MeasurementControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/MeasurementControl.Designer.cs	
@@ -29,12 +29,12 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			this.valValue = new System.Windows.Forms.NumericUpDown();
-			this.radPx = new System.Windows.Forms.RadioButton();
-			this.radPct = new System.Windows.Forms.RadioButton();
-			this.chkCentered = new System.Windows.Forms.CheckBox();
+			this.valValue = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.radPx = new Desktop.Skinning.SkinnedRadioButton();
+			this.radPct = new Desktop.Skinning.SkinnedRadioButton();
+			this.chkCentered = new Desktop.Skinning.SkinnedCheckBox();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
-			this.lblPct = new System.Windows.Forms.Label();
+			this.lblPct = new Desktop.Skinning.SkinnedLabel();
 			((System.ComponentModel.ISupportInitialize)(this.valValue)).BeginInit();
 			this.SuspendLayout();
 			// 
@@ -121,11 +121,11 @@
 
 		#endregion
 
-		private System.Windows.Forms.NumericUpDown valValue;
-		private System.Windows.Forms.RadioButton radPx;
-		private System.Windows.Forms.RadioButton radPct;
-		private System.Windows.Forms.CheckBox chkCentered;
+		private Desktop.Skinning.SkinnedNumericUpDown valValue;
+		private Desktop.Skinning.SkinnedRadioButton radPx;
+		private Desktop.Skinning.SkinnedRadioButton radPct;
+		private Desktop.Skinning.SkinnedCheckBox chkCentered;
 		private System.Windows.Forms.ToolTip toolTip1;
-		private System.Windows.Forms.Label lblPct;
+		private Desktop.Skinning.SkinnedLabel lblPct;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/MeasurementControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/MeasurementControl.cs
index 1379f69fbd407ef8ebecaad10526d4ae9917726e..776763ebfd1dec62932470ce08281b199976bcdc 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/MeasurementControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/MeasurementControl.cs	
@@ -162,7 +162,7 @@ namespace SPNATI_Character_Editor.EditControls
 			Save();
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			RemoveHandlers();
 			radPx.Checked = true;
@@ -171,7 +171,7 @@ namespace SPNATI_Character_Editor.EditControls
 			AddHandlers();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			if (chkCentered.Checked)
 			{
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/NumericRangeControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/NumericRangeControl.Designer.cs
index 128ad463e8be2c93714b47ab274577b81b8de579..35c4c88e436f61bd31a4f9c235030daa1199e1ea 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/NumericRangeControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/NumericRangeControl.Designer.cs	
@@ -28,11 +28,11 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label2 = new System.Windows.Forms.Label();
-			this.label1 = new System.Windows.Forms.Label();
-			this.valFrom = new System.Windows.Forms.NumericUpDown();
-			this.valTo = new System.Windows.Forms.NumericUpDown();
-			this.chkUpper = new System.Windows.Forms.CheckBox();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.valFrom = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.valTo = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.chkUpper = new Desktop.Skinning.SkinnedCheckBox();
 			((System.ComponentModel.ISupportInitialize)(this.valFrom)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.valTo)).BeginInit();
 			this.SuspendLayout();
@@ -41,6 +41,10 @@
 			// 
 			this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left;
 			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label2.Location = new System.Drawing.Point(83, 3);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(19, 13);
@@ -51,6 +55,10 @@
 			// 
 			this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left;
 			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(-2, 3);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(33, 13);
@@ -59,6 +67,9 @@
 			// 
 			// valFrom
 			// 
+			this.valFrom.BackColor = System.Drawing.Color.White;
+			this.valFrom.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valFrom.ForeColor = System.Drawing.Color.Black;
 			this.valFrom.Location = new System.Drawing.Point(37, 1);
 			this.valFrom.Name = "valFrom";
 			this.valFrom.Size = new System.Drawing.Size(43, 20);
@@ -66,6 +77,9 @@
 			// 
 			// valTo
 			// 
+			this.valTo.BackColor = System.Drawing.Color.White;
+			this.valTo.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valTo.ForeColor = System.Drawing.Color.Black;
 			this.valTo.Location = new System.Drawing.Point(106, 1);
 			this.valTo.Name = "valTo";
 			this.valTo.Size = new System.Drawing.Size(41, 20);
@@ -100,10 +114,10 @@
 		}
 
 		#endregion
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.NumericUpDown valFrom;
-		private System.Windows.Forms.NumericUpDown valTo;
-		private System.Windows.Forms.CheckBox chkUpper;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedNumericUpDown valFrom;
+		private Desktop.Skinning.SkinnedNumericUpDown valTo;
+		private Desktop.Skinning.SkinnedCheckBox chkUpper;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/NumericRangeControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/NumericRangeControl.cs
index d52d24e2973846fd127119efca606469eca0a199..bbae5dfd1e6b0b24953cf248601ac7d59bb35358 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/NumericRangeControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/NumericRangeControl.cs	
@@ -105,7 +105,7 @@ namespace SPNATI_Character_Editor
 			}
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			RemoveHandlers();
 			valFrom.Text = "";
@@ -134,7 +134,7 @@ namespace SPNATI_Character_Editor
 			return value;
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			string value = BuildValue();	
 			SetValue(value);
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/OneShotControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/OneShotControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ae5d0532625a7b3cd1f284b2eaedfab53e33dab1
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/OneShotControl.Designer.cs	
@@ -0,0 +1,59 @@
+namespace SPNATI_Character_Editor
+{
+	partial class OneShotControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.chkOneShot = new Desktop.Skinning.SkinnedCheckBox();
+			this.SuspendLayout();
+			// 
+			// chkOneShot
+			// 
+			this.chkOneShot.AutoSize = true;
+			this.chkOneShot.Location = new System.Drawing.Point(0, 4);
+			this.chkOneShot.Name = "chkOneShot";
+			this.chkOneShot.Size = new System.Drawing.Size(15, 14);
+			this.chkOneShot.TabIndex = 0;
+			this.chkOneShot.UseVisualStyleBackColor = true;
+			// 
+			// OneShotControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.chkOneShot);
+			this.Name = "OneShotControl";
+			this.Size = new System.Drawing.Size(150, 21);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedCheckBox chkOneShot;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/OneShotControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/OneShotControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..322d0d8aa2ffeb68cb54429333ceac059f0357f5
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/OneShotControl.cs	
@@ -0,0 +1,132 @@
+using Desktop;
+using Desktop.CommonControls;
+using System;
+using System.Collections.Generic;
+
+namespace SPNATI_Character_Editor
+{
+	public partial class OneShotControl : PropertyEditControl
+	{
+		private OneShotMode _mode;
+		private int _id;
+
+		public OneShotControl()
+		{
+			InitializeComponent();
+		}
+
+		protected override void OnSetParameters(EditControlAttribute parameters)
+		{
+			OneShotAttribute attrib = parameters as OneShotAttribute;
+			_mode = attrib.Mode;
+		}
+
+		protected override void OnBoundData()
+		{
+			int id = (int)GetValue();
+			_id = id;
+
+			//If this is being added, we must always want to play once, so assign an idea right away
+			chkOneShot.Checked = true;
+			Save();
+		}
+
+		protected override void AddHandlers()
+		{
+			chkOneShot.CheckedChanged += ChkOneShot_CheckedChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			chkOneShot.CheckedChanged -= ChkOneShot_CheckedChanged;
+		}
+
+		private void ChkOneShot_CheckedChanged(object sender, EventArgs e)
+		{
+			Save();
+		}
+
+		protected override void OnClear()
+		{
+			RemoveHandlers();
+			chkOneShot.Checked = false;
+			AddHandlers();
+			Save();
+		}
+
+		protected override void OnSave()
+		{
+			if (chkOneShot.Checked)
+			{
+				if (_id == 0)
+				{
+					Character character = Context as Character;
+					if (character == null)
+					{
+						_id = 1;
+					}
+					else
+					{
+						switch (_mode)
+						{
+							case OneShotMode.Case:
+								_id = ++character.Behavior.MaxCaseId;
+								break;
+							case OneShotMode.Stage:
+								_id = ++character.Behavior.MaxStageId;
+								break;
+						}
+					}
+				}
+
+				SetValue(_id);
+			}
+			else
+			{
+				SetValue(0);
+			}
+		}
+
+		public override void BuildMacro(List<string> values)
+		{
+			values.Add(chkOneShot.Checked ? _id.ToString() : "");
+		}
+
+		public override void ApplyMacro(List<string> values)
+		{
+			if (values.Count > 0)
+			{
+				int id;
+				if (int.TryParse(values[0], out id) && id > 0)
+				{
+					chkOneShot.Checked = true;
+				}
+				else
+				{
+					chkOneShot.Checked = false;
+				}
+			}
+		}
+	}
+
+	public class OneShotAttribute : EditControlAttribute
+	{
+		public OneShotMode Mode;
+
+		public override Type EditControlType
+		{
+			get { return typeof(OneShotControl); }
+		}
+
+		public OneShotAttribute(OneShotMode mode)
+		{
+			Mode = mode;
+		}
+	}
+
+	public enum OneShotMode
+	{
+		Case,
+		Stage
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/PersistentMarkerControl.resx b/editor source/SPNATI Character Editor/Controls/EditControls/OneShotControl.resx
similarity index 100%
rename from editor source/SPNATI Character Editor/Controls/EditControls/PersistentMarkerControl.resx
rename to editor source/SPNATI Character Editor/Controls/EditControls/OneShotControl.resx
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/ParticleColorControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/ParticleColorControl.Designer.cs
index 44740f7b43145277101b6e609c0d2b9625ac993d..2788efcfa0ce97f1da40a01b8d840d708d7e76f4 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/ParticleColorControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/ParticleColorControl.Designer.cs	
@@ -29,25 +29,32 @@
 		private void InitializeComponent()
 		{
 			this.txtValue = new Desktop.CommonControls.TextField();
-			this.label1 = new System.Windows.Forms.Label();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
 			this.cmdColor = new System.Windows.Forms.Button();
-			this.label2 = new System.Windows.Forms.Label();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
 			this.txtValue2 = new Desktop.CommonControls.TextField();
-			this.label3 = new System.Windows.Forms.Label();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
 			this.cmdColor2 = new System.Windows.Forms.Button();
 			this.colorPicker = new System.Windows.Forms.ColorDialog();
 			this.SuspendLayout();
 			// 
 			// txtValue
 			// 
+			this.txtValue.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.txtValue.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
 			this.txtValue.Location = new System.Drawing.Point(58, 0);
+			this.txtValue.Multiline = false;
 			this.txtValue.Name = "txtValue";
+			this.txtValue.PlaceholderText = "";
+			this.txtValue.ReadOnly = false;
 			this.txtValue.Size = new System.Drawing.Size(49, 20);
 			this.txtValue.TabIndex = 6;
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(44, 3);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(14, 13);
@@ -68,6 +75,10 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label2.Location = new System.Drawing.Point(113, 3);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(16, 13);
@@ -76,14 +87,21 @@
 			// 
 			// txtValue2
 			// 
+			this.txtValue2.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.txtValue2.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
 			this.txtValue2.Location = new System.Drawing.Point(190, 0);
+			this.txtValue2.Multiline = false;
 			this.txtValue2.Name = "txtValue2";
+			this.txtValue2.PlaceholderText = "";
+			this.txtValue2.ReadOnly = false;
 			this.txtValue2.Size = new System.Drawing.Size(49, 20);
 			this.txtValue2.TabIndex = 10;
 			// 
 			// label3
 			// 
 			this.label3.AutoSize = true;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label3.Location = new System.Drawing.Point(176, 3);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(14, 13);
@@ -127,11 +145,11 @@
 		#endregion
 
 		private Desktop.CommonControls.TextField txtValue;
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedLabel label1;
 		private System.Windows.Forms.Button cmdColor;
-		private System.Windows.Forms.Label label2;
+		private Desktop.Skinning.SkinnedLabel label2;
 		private Desktop.CommonControls.TextField txtValue2;
-		private System.Windows.Forms.Label label3;
+		private Desktop.Skinning.SkinnedLabel label3;
 		private System.Windows.Forms.Button cmdColor2;
 		private System.Windows.Forms.ColorDialog colorPicker;
 	}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/ParticleColorControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/ParticleColorControl.cs
index 2ca9920959760a41f6452b09aa9b1babaca2a2e7..0eccc652ff8fc2788c261fe6594663655f462eb2 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/ParticleColorControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/ParticleColorControl.cs	
@@ -108,7 +108,7 @@ namespace SPNATI_Character_Editor.Controls.EditControls
 			catch { }
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			RemoveHandlers();
 			cmdColor.BackColor = Color.Transparent;
@@ -119,7 +119,7 @@ namespace SPNATI_Character_Editor.Controls.EditControls
 			Save();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			string color1 = txtValue.Text;
 			string color2 = txtValue2.Text;
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/ParticleFloatControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/ParticleFloatControl.Designer.cs
index afe24a487fe7a347b186c1badb9f99ef2a462a94..04fd8b00f30fbf9c8f6f8b255ae44a8d9a6bdfde 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/ParticleFloatControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/ParticleFloatControl.Designer.cs	
@@ -28,15 +28,18 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.valFrom = new System.Windows.Forms.NumericUpDown();
-			this.label1 = new System.Windows.Forms.Label();
-			this.valTo = new System.Windows.Forms.NumericUpDown();
+			this.valFrom = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.valTo = new Desktop.Skinning.SkinnedNumericUpDown();
 			((System.ComponentModel.ISupportInitialize)(this.valFrom)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.valTo)).BeginInit();
 			this.SuspendLayout();
 			// 
 			// valFrom
 			// 
+			this.valFrom.BackColor = System.Drawing.Color.White;
+			this.valFrom.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valFrom.ForeColor = System.Drawing.Color.Black;
 			this.valFrom.Location = new System.Drawing.Point(0, 0);
 			this.valFrom.Name = "valFrom";
 			this.valFrom.Size = new System.Drawing.Size(53, 20);
@@ -45,6 +48,10 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(56, 2);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(19, 13);
@@ -53,6 +60,9 @@
 			// 
 			// valTo
 			// 
+			this.valTo.BackColor = System.Drawing.Color.White;
+			this.valTo.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valTo.ForeColor = System.Drawing.Color.Black;
 			this.valTo.Location = new System.Drawing.Point(77, 0);
 			this.valTo.Name = "valTo";
 			this.valTo.Size = new System.Drawing.Size(53, 20);
@@ -76,8 +86,8 @@
 
 		#endregion
 
-		private System.Windows.Forms.NumericUpDown valFrom;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.NumericUpDown valTo;
+		private Desktop.Skinning.SkinnedNumericUpDown valFrom;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedNumericUpDown valTo;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/ParticleFloatControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/ParticleFloatControl.cs
index 4971150415db8f6b39918530bb0abd501976289f..eb8f61e83e1f4165832d42c6eafb4399c0c093ee 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/ParticleFloatControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/ParticleFloatControl.cs	
@@ -98,7 +98,7 @@ namespace SPNATI_Character_Editor.Controls.EditControls
 			}
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			RemoveHandlers();
 			valFrom.Value = Math.Max(0, valFrom.Minimum);
@@ -109,7 +109,7 @@ namespace SPNATI_Character_Editor.Controls.EditControls
 			AddHandlers();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			if (valFrom.Text == "")
 			{
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/PersistentMarkerControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/PersistentMarkerControl.Designer.cs
deleted file mode 100644
index 88850b1421d4c652ed43c40c0cc07467edc6377e..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/PersistentMarkerControl.Designer.cs	
+++ /dev/null
@@ -1,94 +0,0 @@
-namespace SPNATI_Character_Editor
-{
-	partial class PersistentMarkerControl
-	{
-		/// <summary> 
-		/// Required designer variable.
-		/// </summary>
-		private System.ComponentModel.IContainer components = null;
-
-		/// <summary> 
-		/// Clean up any resources being used.
-		/// </summary>
-		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
-		protected override void Dispose(bool disposing)
-		{
-			if (disposing && (components != null))
-			{
-				components.Dispose();
-			}
-			base.Dispose(disposing);
-		}
-
-		#region Component Designer generated code
-
-		/// <summary> 
-		/// Required method for Designer support - do not modify 
-		/// the contents of this method with the code editor.
-		/// </summary>
-		private void InitializeComponent()
-		{
-			this.recField = new Desktop.CommonControls.RecordField();
-			this.txtValue = new System.Windows.Forms.TextBox();
-			this.cboOperator = new System.Windows.Forms.ComboBox();
-			this.SuspendLayout();
-			// 
-			// recField
-			// 
-			this.recField.AllowCreate = true;
-			this.recField.Location = new System.Drawing.Point(0, 1);
-			this.recField.Name = "recField";
-			this.recField.PlaceholderText = null;
-			this.recField.Record = null;
-			this.recField.RecordContext = null;
-			this.recField.RecordKey = null;
-			this.recField.RecordType = null;
-			this.recField.Size = new System.Drawing.Size(150, 20);
-			this.recField.TabIndex = 0;
-			this.recField.UseAutoComplete = false;
-			// 
-			// txtValue
-			// 
-			this.txtValue.Location = new System.Drawing.Point(218, 0);
-			this.txtValue.Name = "txtValue";
-			this.txtValue.Size = new System.Drawing.Size(100, 20);
-			this.txtValue.TabIndex = 5;
-			// 
-			// cboOperator
-			// 
-			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
-			this.cboOperator.FormattingEnabled = true;
-			this.cboOperator.Items.AddRange(new object[] {
-            "",
-            "==",
-            "!=",
-            "<",
-            ">",
-            "<=",
-            ">="});
-			this.cboOperator.Location = new System.Drawing.Point(156, 0);
-			this.cboOperator.Name = "cboOperator";
-			this.cboOperator.Size = new System.Drawing.Size(56, 21);
-			this.cboOperator.TabIndex = 4;
-			// 
-			// PersistentMarkerControl
-			// 
-			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.txtValue);
-			this.Controls.Add(this.cboOperator);
-			this.Controls.Add(this.recField);
-			this.Name = "PersistentMarkerControl";
-			this.Size = new System.Drawing.Size(605, 21);
-			this.ResumeLayout(false);
-			this.PerformLayout();
-
-		}
-
-		#endregion
-
-		private Desktop.CommonControls.RecordField recField;
-		private System.Windows.Forms.TextBox txtValue;
-		private System.Windows.Forms.ComboBox cboOperator;
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/PersistentMarkerControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/PersistentMarkerControl.cs
deleted file mode 100644
index 7e0a3c984ace64d82814e01386ed514300a4c320..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/PersistentMarkerControl.cs	
+++ /dev/null
@@ -1,175 +0,0 @@
-using Desktop;
-using Desktop.CommonControls;
-using System;
-using System.Text.RegularExpressions;
-using System.Collections.Generic;
-
-namespace SPNATI_Character_Editor
-{
-	public partial class PersistentMarkerControl : SubVariableControl
-	{
-		private ExpressionTest _expression;
-		private string _wildcardVariable = "~persistent.*~";
-		private TargetMode _mode = TargetMode.Self;
-
-		public PersistentMarkerControl()
-		{
-			InitializeComponent();
-
-			recField.RecordType = typeof(Marker);
-		}
-
-		public override void BuildMacro(List<string> values)
-		{
-			Save();
-			values.Add(_expression.Expression);
-			values.Add(_expression.Operator);
-			values.Add(_expression.Value);
-		}
-
-		public override void OnInitialAdd()
-		{
-			if (recField.RecordContext != null)
-			{
-				recField.DoSearch();
-			}
-		}
-
-		public override void OnAddedToRow()
-		{
-			OnChangeLabel(_mode == TargetMode.Self ? "Marker (Persistent)" : _mode == TargetMode.Target ? "Target Marker (Persistent)" : "Also Playing Marker (Persistent)");
-		}
-
-		private bool FilterPrivateMarkers(IRecord record)
-		{
-			Marker marker = record as Marker;
-			return marker.Scope == MarkerScope.Public;
-		}
-
-		protected override void OnBoundData()
-		{
-			_expression = GetValue() as ExpressionTest;
-
-			if (_expression.Expression.StartsWith("~persistent."))
-			{
-				_mode = TargetMode.Self;
-				recField.RecordContext = Context;
-				_wildcardVariable = "~persistent.*~";
-			}
-			else if (_expression.Expression.StartsWith("~target.persistent."))
-			{
-				_mode = TargetMode.Target;
-				recField.RecordFilter = FilterPrivateMarkers;
-				SetTargetContext();
-				_wildcardVariable = "~target.persistent.*~";
-			}
-
-			recField.RecordKey = null;
-			recField.UseAutoComplete = true;
-
-			string pattern = _wildcardVariable.Replace("*", "([^~]*)");
-			Match match = Regex.Match(_expression.Expression, pattern);
-			if (match.Success)
-			{
-				string key = match.Groups[1].Value;
-				if (!string.IsNullOrEmpty(key) && key != "*")
-				{
-					recField.RecordKey = key;
-				}
-			}
-
-			cboOperator.Text = _expression.Operator;
-			txtValue.Text = _expression.Value;
-			OnAddedToRow();
-		}
-
-		protected override void OnBindingUpdated(string property)
-		{
-			if (property == "Target" && _mode == TargetMode.Target)
-			{
-				SetTargetContext();
-			}
-		}
-
-		private void SetTargetContext()
-		{
-			Case context = Data as Case;
-			string target = context.Target;
-			if (!string.IsNullOrEmpty(target))
-			{
-				Character targetChar = CharacterDatabase.Get(target);
-				recField.RecordContext = targetChar;
-			}
-			else
-			{
-				recField.RecordContext = null;
-			}
-		}
-
-		protected override void RemoveHandlers()
-		{
-			recField.RecordChanged -= RecordChanged;
-			cboOperator.SelectedIndexChanged -= ValueChanged;
-			txtValue.TextChanged -= ValueChanged;
-		}
-
-		protected override void AddHandlers()
-		{
-			recField.RecordChanged += RecordChanged;
-			cboOperator.SelectedIndexChanged += ValueChanged;
-			txtValue.TextChanged += ValueChanged;
-		}
-
-		public override void Clear()
-		{
-			RemoveHandlers();
-			recField.RecordKey = null;
-			cboOperator.SelectedIndex = 0;
-			txtValue.Text = "";
-			AddHandlers();
-			Save();
-		}
-
-		public override void Save()
-		{
-			string key = recField.RecordKey;
-			if (string.IsNullOrEmpty(key))
-			{
-				key = "*";
-			}
-
-			string expression = _wildcardVariable.Replace("*", key);
-			_expression.Expression = expression;
-
-			string op = cboOperator.SelectedItem?.ToString();
-			if (string.IsNullOrEmpty(op))
-			{
-				op = ">";
-			}
-			_expression.Operator = op;
-
-			string value = txtValue.Text;
-			if (string.IsNullOrEmpty(value))
-			{
-				value = "0";
-			}
-			_expression.Value = value;
-		}
-
-		private void ValueChanged(object sender, EventArgs e)
-		{
-			Save();
-		}
-
-		private void RecordChanged(object sender, RecordEventArgs record)
-		{
-			Save();
-		}
-
-		private enum TargetMode
-		{
-			Self,
-			Target
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/PlacementControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/PlacementControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..242bcf3a05fc8fca24f0e37a24f6527028eb0660
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/PlacementControl.cs	
@@ -0,0 +1,189 @@
+using System;
+
+namespace SPNATI_Character_Editor
+{
+	[SubVariable("place")]
+	[SubVariable("revplace")]
+	[SubVariable("lead")]
+	[SubVariable("biggestlead")]
+	[SubVariable("trail")]
+	public class PlacementControl : PlayerControlBase
+	{
+		private Desktop.Skinning.SkinnedComboBox cboPlace;
+		private Desktop.Skinning.SkinnedTextBox txtValue;
+		private Desktop.Skinning.SkinnedComboBox cboOperator;
+
+		private static readonly PlacementVariable[] _variables =
+		{
+			new PlacementVariable("place", "Place (1=first, 5=last)"),
+			new PlacementVariable("revplace", "Reverse Place (1=last, 5=first)"),
+			new PlacementVariable("lead", "Layers in lead"),
+			new PlacementVariable("trail", "Layers behind"),
+			new PlacementVariable("biggestlead", "Largest lead"),
+		};
+
+		public PlacementControl()
+		{
+			InitializeComponent();
+			cboOperator.DataSource = ExpressionTest.Operators;
+			
+			cboPlace.DataSource = _variables;
+			Bindings.Add("AlsoPlaying");
+		}
+
+		private void InitializeComponent()
+		{
+			this.cboPlace = new Desktop.Skinning.SkinnedComboBox();
+			this.txtValue = new Desktop.Skinning.SkinnedTextBox();
+			this.cboOperator = new Desktop.Skinning.SkinnedComboBox();
+			this.SuspendLayout();
+			// 
+			// cboPlace
+			// 
+			this.cboPlace.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboPlace.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboPlace.BackColor = System.Drawing.Color.White;
+			this.cboPlace.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboPlace.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboPlace.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboPlace.FormattingEnabled = true;
+			this.cboPlace.Location = new System.Drawing.Point(161, 0);
+			this.cboPlace.Name = "cboPlace";
+			this.cboPlace.SelectedIndex = -1;
+			this.cboPlace.SelectedItem = null;
+			this.cboPlace.Size = new System.Drawing.Size(100, 21);
+			this.cboPlace.Sorted = false;
+			this.cboPlace.TabIndex = 11;
+			// 
+			// txtValue
+			// 
+			this.txtValue.BackColor = System.Drawing.Color.White;
+			this.txtValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtValue.ForeColor = System.Drawing.Color.Black;
+			this.txtValue.Location = new System.Drawing.Point(324, 0);
+			this.txtValue.Name = "txtValue";
+			this.txtValue.Size = new System.Drawing.Size(100, 20);
+			this.txtValue.TabIndex = 13;
+			// 
+			// cboOperator
+			// 
+			this.cboOperator.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboOperator.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboOperator.BackColor = System.Drawing.Color.White;
+			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboOperator.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboOperator.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboOperator.FormattingEnabled = true;
+			this.cboOperator.Location = new System.Drawing.Point(264, 0);
+			this.cboOperator.Name = "cboOperator";
+			this.cboOperator.SelectedIndex = -1;
+			this.cboOperator.SelectedItem = null;
+			this.cboOperator.Size = new System.Drawing.Size(56, 21);
+			this.cboOperator.Sorted = false;
+			this.cboOperator.TabIndex = 12;
+			// 
+			// PlacementControl
+			// 
+			this.Controls.Add(this.txtValue);
+			this.Controls.Add(this.cboOperator);
+			this.Controls.Add(this.cboPlace);
+			this.Name = "PlacementControl";
+			this.Size = new System.Drawing.Size(503, 21);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		protected override void AddHandlers()
+		{
+			base.AddHandlers();
+			cboOperator.SelectedIndexChanged += ValueChanged;
+			cboPlace.SelectedIndexChanged += ValueChanged;
+			txtValue.TextChanged += ValueChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			base.RemoveHandlers();
+			cboOperator.SelectedIndexChanged -= ValueChanged;
+			cboPlace.SelectedIndexChanged -= ValueChanged;
+			txtValue.TextChanged -= ValueChanged;
+		}
+
+		private void ValueChanged(object sender, EventArgs e)
+		{
+			Save();
+		}
+
+		protected override void OnBoundData()
+		{
+			base.OnBoundData();
+			cboOperator.Text = Expression.Operator ?? "==";
+			txtValue.Text = Expression.Value;
+			OnAddedToRow();
+		}
+
+		protected override void BindVariable(string variable)
+		{
+			for (int i = 0; i < _variables.Length; i++)
+			{
+				if (_variables[i].Variable == variable)
+				{
+					cboPlace.SelectedIndex = i;
+					break;
+				}
+			}
+		}
+
+		public override void OnAddedToRow()
+		{
+			OnChangeLabel("Place Check");
+		}
+
+		protected override void OnSave()
+		{
+			base.OnSave();
+			string op = cboOperator.SelectedItem?.ToString();
+			if (string.IsNullOrEmpty(op))
+			{
+				op = "==";
+			}
+			Expression.Operator = op;
+
+			string value = txtValue.Text;
+			if (string.IsNullOrEmpty(value))
+			{
+				value = null;
+			}
+			Expression.Value = value;
+		}
+
+		protected override string GetVariable()
+		{
+			string v = "";
+			PlacementVariable variable = cboPlace.SelectedItem as PlacementVariable;
+			if (variable != null)
+			{
+				v = variable.Variable;
+			}
+			return v;
+		}
+
+		private class PlacementVariable
+		{
+			public string Variable;
+			public string DisplayName;
+
+			public PlacementVariable(string variable, string name)
+			{
+				Variable = variable;
+				DisplayName = name;
+			}
+
+			public override string ToString()
+			{
+				return DisplayName;
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/TagControl.resx b/editor source/SPNATI Character Editor/Controls/EditControls/PlacementControl.resx
similarity index 100%
rename from editor source/SPNATI Character Editor/Controls/EditControls/TagControl.resx
rename to editor source/SPNATI Character Editor/Controls/EditControls/PlacementControl.resx
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/StageControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/StageControl.Designer.cs
index a81857e24875747913eadbc90bd82ebb92d086f3..1c166d2eb8e27ead5dbea93b806b66a314f07a7e 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/StageControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/StageControl.Designer.cs	
@@ -28,26 +28,35 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.cboFrom = new System.Windows.Forms.ComboBox();
-			this.label1 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
-			this.cboTo = new System.Windows.Forms.ComboBox();
+			this.cboFrom = new Desktop.Skinning.SkinnedComboBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.cboTo = new Desktop.Skinning.SkinnedComboBox();
 			this.SuspendLayout();
 			// 
 			// cboFrom
 			// 
 			this.cboFrom.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
-			this.cboFrom.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems;
+			this.cboFrom.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
+			this.cboFrom.BackColor = System.Drawing.Color.White;
+			this.cboFrom.DropDownStyle = System.Windows.Forms.ComboBoxStyle.Simple;
+			this.cboFrom.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboFrom.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboFrom.FormattingEnabled = true;
 			this.cboFrom.Location = new System.Drawing.Point(42, 0);
 			this.cboFrom.Name = "cboFrom";
+			this.cboFrom.SelectedIndex = -1;
+			this.cboFrom.SelectedItem = null;
 			this.cboFrom.Size = new System.Drawing.Size(150, 21);
+			this.cboFrom.Sorted = false;
 			this.cboFrom.TabIndex = 0;
 			// 
 			// label1
 			// 
 			this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left;
 			this.label1.AutoSize = true;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(3, 3);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(33, 13);
@@ -58,6 +67,8 @@
 			// 
 			this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left;
 			this.label2.AutoSize = true;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label2.Location = new System.Drawing.Point(198, 3);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(19, 13);
@@ -67,11 +78,18 @@
 			// cboTo
 			// 
 			this.cboTo.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
-			this.cboTo.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems;
+			this.cboTo.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
+			this.cboTo.BackColor = System.Drawing.Color.White;
+			this.cboTo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.Simple;
+			this.cboTo.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboTo.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboTo.FormattingEnabled = true;
 			this.cboTo.Location = new System.Drawing.Point(223, 0);
 			this.cboTo.Name = "cboTo";
+			this.cboTo.SelectedIndex = -1;
+			this.cboTo.SelectedItem = null;
 			this.cboTo.Size = new System.Drawing.Size(150, 21);
+			this.cboTo.Sorted = false;
 			this.cboTo.TabIndex = 3;
 			// 
 			// StageControl
@@ -91,9 +109,9 @@
 
 		#endregion
 
-		private System.Windows.Forms.ComboBox cboFrom;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.ComboBox cboTo;
+		private Desktop.Skinning.SkinnedComboBox cboFrom;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedComboBox cboTo;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/StageControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/StageControl.cs
index 6127b502e370c3e67d6f5cb00fa332384c55e045..feaf2614199410a2d2b79f4ee9363f09600982b5 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/StageControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/StageControl.cs	
@@ -1,5 +1,6 @@
 using Desktop;
 using Desktop.CommonControls;
+using Desktop.Skinning;
 using System;
 using System.Collections.Generic;
 using System.Reflection;
@@ -76,7 +77,7 @@ namespace SPNATI_Character_Editor
 			SetCombo(cboTo, max);
 		}
 
-		private void SetCombo(ComboBox box, string stage)
+		private void SetCombo(SkinnedComboBox box, string stage)
 		{
 			for (int i = 0; i < box.Items.Count; i++)
 			{
@@ -97,9 +98,6 @@ namespace SPNATI_Character_Editor
 
 		private void FillItems()
 		{
-			string from = cboFrom.Text;
-			string to = cboTo.Text;
-
 			Case selectedCase = Data as Case;
 
 			string key = _sourceMember.GetValue(Data)?.ToString();
@@ -189,22 +187,6 @@ namespace SPNATI_Character_Editor
 				}
 				cboFrom.DataSource = data;
 				cboTo.DataSource = data;
-				if (!string.IsNullOrEmpty(from))
-				{
-					cboFrom.Text = from;
-				}
-				else
-				{
-					cboFrom.Text = "";
-				}
-				if (!string.IsNullOrEmpty(to))
-				{
-					cboTo.Text = to;
-				}
-				else
-				{
-					cboTo.Text = "";
-				}
 			}
 		}
 
@@ -266,7 +248,7 @@ namespace SPNATI_Character_Editor
 			return character;
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			cboFrom.Text = "";
 			cboTo.Text = "";
@@ -290,13 +272,13 @@ namespace SPNATI_Character_Editor
 			return value;
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			string value = BuildValue();
 			SetValue(value);
 		}
 
-		private string ReadStage(ComboBox box)
+		private string ReadStage(SkinnedComboBox box)
 		{
 			if (string.IsNullOrEmpty(box.Text))
 			{
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/StatusControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/StatusControl.Designer.cs
index 7bb40d76cc033b5127115b1806da37fceefddcb2..0fb4990269c91c9b4c52b675087e136df0c90dd7 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/StatusControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/StatusControl.Designer.cs	
@@ -28,8 +28,8 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.chkNegate = new System.Windows.Forms.CheckBox();
-			this.cboStatus = new System.Windows.Forms.ComboBox();
+			this.chkNegate = new Desktop.Skinning.SkinnedCheckBox();
+			this.cboStatus = new Desktop.Skinning.SkinnedComboBox();
 			this.SuspendLayout();
 			// 
 			// chkNegate
@@ -47,12 +47,18 @@
 			// 
 			this.cboStatus.Anchor = System.Windows.Forms.AnchorStyles.Left;
 			this.cboStatus.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
-			this.cboStatus.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems;
+			this.cboStatus.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
+			this.cboStatus.BackColor = System.Drawing.Color.White;
 			this.cboStatus.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboStatus.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboStatus.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboStatus.FormattingEnabled = true;
 			this.cboStatus.Location = new System.Drawing.Point(52, 0);
 			this.cboStatus.Name = "cboStatus";
+			this.cboStatus.SelectedIndex = -1;
+			this.cboStatus.SelectedItem = null;
 			this.cboStatus.Size = new System.Drawing.Size(193, 21);
+			this.cboStatus.Sorted = false;
 			this.cboStatus.TabIndex = 34;
 			// 
 			// StatusControl
@@ -70,7 +76,7 @@
 
 		#endregion
 
-		private System.Windows.Forms.CheckBox chkNegate;
-		private System.Windows.Forms.ComboBox cboStatus;
+		private Desktop.Skinning.SkinnedCheckBox chkNegate;
+		private Desktop.Skinning.SkinnedComboBox cboStatus;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/StatusControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/StatusControl.cs
index 536fca9ccab993f3cefbd70cc4afd7da0bfc0a08..9716dd420b23ac2c939b27dec47fa4be7eb9556c 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/StatusControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/StatusControl.cs	
@@ -52,7 +52,7 @@ namespace SPNATI_Character_Editor
 			return value;
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			chkNegate.Checked = false;
 			cboStatus.SelectedIndex = 0;
@@ -77,7 +77,7 @@ namespace SPNATI_Character_Editor
 			}
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			string value = BuildValue();
 			SetValue(value);
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/TagControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/TagControl.cs
deleted file mode 100644
index c650b3b8f0b9e318319cadc7dafc9ba51892adc3..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/EditControls/TagControl.cs	
+++ /dev/null
@@ -1,142 +0,0 @@
-using System.Text.RegularExpressions;
-using System.Collections.Generic;
-
-namespace SPNATI_Character_Editor
-{
-	public partial class TagControl : SubVariableControl
-	{
-		private ExpressionTest _expression;
-		private string _wildcardVariable = "~self.tag.*~";
-		private TargetMode _mode = TargetMode.Self;
-
-		public TagControl()
-		{
-			InitializeComponent();
-
-			recField.RecordType = typeof(Tag);
-		}
-
-		protected override void OnBoundData()
-		{
-			_expression = GetValue() as ExpressionTest;
-
-			if (_expression.Expression.StartsWith("~self.tag."))
-			{
-				_mode = TargetMode.Self;
-				recField.RecordContext = Context;
-				_wildcardVariable = "~self.tag.*~";
-			}
-			else if (_expression.Expression.StartsWith("~target.tag."))
-			{
-				_mode = TargetMode.Target;
-				SetTargetContext();
-				_wildcardVariable = "~target.tag.*~";
-			}
-			recField.RecordKey = null;
-
-			string pattern = _wildcardVariable.Replace("*", "([^~]*)");
-			Match match = Regex.Match(_expression.Expression, pattern);
-			if (match.Success)
-			{
-				string key = match.Groups[1].Value;
-				if (!string.IsNullOrEmpty(key) && key != "*")
-				{
-					recField.RecordKey = key;
-				}
-			}
-
-			chkNot.Checked = _expression.Value == "false";
-			OnAddedToRow();
-		}
-
-		protected override void OnBindingUpdated(string property)
-		{
-			if (property == "Target" && _mode == TargetMode.Target)
-			{
-				SetTargetContext();
-			}
-		}
-
-		private void SetTargetContext()
-		{
-			Case context = Data as Case;
-			string target = context.Target;
-			if (!string.IsNullOrEmpty(target))
-			{
-				Character targetChar = CharacterDatabase.Get(target);
-				recField.RecordContext = targetChar;
-			}
-			else
-			{
-				recField.RecordContext = null;
-			}
-		}
-
-		public override void OnAddedToRow()
-		{
-			OnChangeLabel(_mode == TargetMode.Self ? "Tag" : _mode == TargetMode.Target ? "Target Tag" : "Also Playing Tag");
-		}
-
-		protected override void AddHandlers()
-		{
-			recField.RecordChanged += RecField_RecordChanged;
-			chkNot.CheckedChanged += Field_CheckedChanged;
-		}
-
-		protected override void RemoveHandlers()
-		{
-			recField.RecordChanged -= RecField_RecordChanged;
-			chkNot.CheckedChanged -= Field_CheckedChanged;
-		}
-
-		public override void ApplyMacro(List<string> values)
-		{
-			//macros should never be applied directly to a subcontrol
-		}
-
-		public override void BuildMacro(List<string> values)
-		{
-			Save();
-			values.Add(_expression.Expression);
-			values.Add(_expression.Operator);
-			values.Add(_expression.Value);
-		}
-
-		private void RecField_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
-		{
-			Save();	
-		}
-		private void Field_CheckedChanged(object sender, System.EventArgs e)
-		{
-			Save();
-		}
-
-		public override void Save()
-		{
-			string key = recField.RecordKey;
-			if (string.IsNullOrEmpty(key))
-			{
-				key = "*";
-			}
-			string expression = _wildcardVariable.Replace("*", key);
-			_expression.Expression = expression;
-			_expression.Operator = "==";
-			if (chkNot.Checked)
-			{
-				_expression.Value = "false";
-			}
-			else
-			{
-				_expression.Value = "true";
-			}
-
-			base.Save();
-		}
-
-		private enum TargetMode
-		{
-			Self,
-			Target
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CategoryControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CategoryControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..49400a10eb09e205f1dac2fdc6b63e10ac2ae55c
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CategoryControl.Designer.cs	
@@ -0,0 +1,86 @@
+namespace SPNATI_Character_Editor.Controls.EditControls.VariableControls
+{
+	partial class CategoryControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.cboOperator = new Desktop.Skinning.SkinnedComboBox();
+			this.recCategory = new Desktop.CommonControls.RecordField();
+			this.SuspendLayout();
+			// 
+			// cboOperator
+			// 
+			this.cboOperator.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboOperator.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboOperator.BackColor = System.Drawing.Color.White;
+			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboOperator.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboOperator.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboOperator.FormattingEnabled = true;
+			this.cboOperator.Location = new System.Drawing.Point(148, 0);
+			this.cboOperator.Name = "cboOperator";
+			this.cboOperator.SelectedIndex = -1;
+			this.cboOperator.SelectedItem = null;
+			this.cboOperator.Size = new System.Drawing.Size(44, 21);
+			this.cboOperator.Sorted = false;
+			this.cboOperator.TabIndex = 13;
+			// 
+			// recCategory
+			// 
+			this.recCategory.AllowCreate = false;
+			this.recCategory.Location = new System.Drawing.Point(198, 0);
+			this.recCategory.Name = "recCategory";
+			this.recCategory.PlaceholderText = null;
+			this.recCategory.Record = null;
+			this.recCategory.RecordContext = null;
+			this.recCategory.RecordFilter = null;
+			this.recCategory.RecordKey = null;
+			this.recCategory.RecordType = null;
+			this.recCategory.Size = new System.Drawing.Size(135, 20);
+			this.recCategory.TabIndex = 14;
+			this.recCategory.UseAutoComplete = false;
+			// 
+			// CategoryControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.recCategory);
+			this.Controls.Add(this.cboOperator);
+			this.Name = "CategoryControl";
+			this.Controls.SetChildIndex(this.cboOperator, 0);
+			this.Controls.SetChildIndex(this.recCategory, 0);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedComboBox cboOperator;
+		private Desktop.CommonControls.RecordField recCategory;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CategoryControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CategoryControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6512050b676c37c51993027e974660c755afe12d
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CategoryControl.cs	
@@ -0,0 +1,96 @@
+using System;
+
+namespace SPNATI_Character_Editor.Controls.EditControls.VariableControls
+{
+	public partial class CategoryControl : PlayerControlBase
+	{
+		public CategoryControl()
+		{
+			InitializeComponent();
+			cboOperator.DataSource = ExpressionTest.Operators;
+			recCategory.RecordType = GetCategoryType();
+		}
+
+		protected virtual Type GetCategoryType()
+		{
+			return null;
+		}
+
+		protected override void OnBoundData()
+		{
+			base.OnBoundData();
+			cboOperator.SelectedItem = Expression.Operator ?? "==";
+			recCategory.RecordKey = Expression.Value;
+		}
+
+		protected override void AddHandlers()
+		{
+			base.AddHandlers();
+			recCategory.RecordChanged += RecCategory_RecordChanged;
+			cboOperator.SelectedIndexChanged += CboOperator_ValueChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			base.AddHandlers();
+			recCategory.RecordChanged -= RecCategory_RecordChanged;
+			cboOperator.SelectedIndexChanged -= CboOperator_ValueChanged;
+		}
+
+		private void RecCategory_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
+		{
+			Save();
+		}
+		private void CboOperator_ValueChanged(object sender, System.EventArgs e)
+		{
+			Save();
+		}
+
+		protected override void OnSave()
+		{
+			base.OnSave();
+			Expression.Operator = cboOperator.Text;
+			Expression.Value = recCategory.RecordKey;
+		}
+	}
+
+	[SubVariable("slot")]
+	public class SlotControl : CategoryControl
+	{
+		protected override Type GetCategoryType()
+		{
+			return typeof(Slot);
+		}
+
+		protected override string GetVariable()
+		{
+			return "slot";
+		}
+
+		public override void OnAddedToRow()
+		{
+			OnChangeLabel("Game Slot");
+		}
+
+	}
+
+	[SubVariable("position")]
+	public class PositionControl : CategoryControl
+	{
+		protected override Type GetCategoryType()
+		{
+			return typeof(RelativePosition);
+		}
+
+		protected override string GetVariable()
+		{
+			return "position";
+		}
+
+		public override void OnAddedToRow()
+		{
+			OnChangeLabel("Relative Position");
+		}
+
+	}
+}
diff --git a/editor source/SPNATI Character Editor/FileNameSelect.resx b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CategoryControl.resx
similarity index 100%
rename from editor source/SPNATI Character Editor/FileNameSelect.resx
rename to editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CategoryControl.resx
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleControl.Designer.cs
similarity index 60%
rename from editor source/SPNATI Character Editor/Controls/EditControls/CollectibleControl.Designer.cs
rename to editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleControl.Designer.cs
index 71c3da6602944992179ade8f0cd1f19d9c6af5cc..ea6226a69f04e89bde56eab356e1fa76b0cc1e5a 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleControl.Designer.cs	
@@ -29,29 +29,34 @@
 		private void InitializeComponent()
 		{
 			this.recField = new Desktop.CommonControls.RecordField();
-			this.label1 = new System.Windows.Forms.Label();
-			this.radUnlocked = new System.Windows.Forms.RadioButton();
-			this.radLocked = new System.Windows.Forms.RadioButton();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.radUnlocked = new Desktop.Skinning.SkinnedRadioButton();
+			this.radLocked = new Desktop.Skinning.SkinnedRadioButton();
 			this.SuspendLayout();
 			// 
 			// recField
 			// 
 			this.recField.AllowCreate = false;
-			this.recField.Location = new System.Drawing.Point(63, 1);
+			this.recField.Location = new System.Drawing.Point(206, 1);
 			this.recField.Name = "recField";
 			this.recField.PlaceholderText = null;
 			this.recField.Record = null;
 			this.recField.RecordContext = null;
+			this.recField.RecordFilter = null;
 			this.recField.RecordKey = null;
 			this.recField.RecordType = null;
-			this.recField.Size = new System.Drawing.Size(150, 20);
-			this.recField.TabIndex = 0;
+			this.recField.Size = new System.Drawing.Size(118, 20);
+			this.recField.TabIndex = 1;
 			this.recField.UseAutoComplete = false;
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(3, 4);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(148, 4);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(58, 13);
 			this.label1.TabIndex = 1;
@@ -60,7 +65,9 @@
 			// radUnlocked
 			// 
 			this.radUnlocked.AutoSize = true;
-			this.radUnlocked.Location = new System.Drawing.Point(220, 2);
+			this.radUnlocked.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.radUnlocked.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.radUnlocked.Location = new System.Drawing.Point(327, 2);
 			this.radUnlocked.Name = "radUnlocked";
 			this.radUnlocked.Size = new System.Drawing.Size(71, 17);
 			this.radUnlocked.TabIndex = 2;
@@ -71,7 +78,9 @@
 			// radLocked
 			// 
 			this.radLocked.AutoSize = true;
-			this.radLocked.Location = new System.Drawing.Point(297, 2);
+			this.radLocked.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.radLocked.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.radLocked.Location = new System.Drawing.Point(398, 2);
 			this.radLocked.Name = "radLocked";
 			this.radLocked.Size = new System.Drawing.Size(61, 17);
 			this.radLocked.TabIndex = 3;
@@ -88,7 +97,11 @@
 			this.Controls.Add(this.label1);
 			this.Controls.Add(this.recField);
 			this.Name = "CollectibleControl";
-			this.Size = new System.Drawing.Size(433, 21);
+			this.Size = new System.Drawing.Size(633, 21);
+			this.Controls.SetChildIndex(this.recField, 0);
+			this.Controls.SetChildIndex(this.label1, 0);
+			this.Controls.SetChildIndex(this.radUnlocked, 0);
+			this.Controls.SetChildIndex(this.radLocked, 0);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -97,8 +110,8 @@
 		#endregion
 
 		private Desktop.CommonControls.RecordField recField;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.RadioButton radUnlocked;
-		private System.Windows.Forms.RadioButton radLocked;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedRadioButton radUnlocked;
+		private Desktop.Skinning.SkinnedRadioButton radLocked;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8050787ed763fc0333cb44be44a75be92ae37e2b
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleControl.cs	
@@ -0,0 +1,97 @@
+using SPNATI_Character_Editor.DataStructures;
+
+namespace SPNATI_Character_Editor
+{
+	[SubVariable("collectible")]
+	public partial class CollectibleControl : PlayerControlBase
+	{
+		public CollectibleControl()
+		{
+			InitializeComponent();
+
+			recField.RecordType = typeof(Collectible);
+		}
+
+		protected override void OnBoundData()
+		{
+			base.OnBoundData();
+			radLocked.Checked = Expression.Value == "false";
+			radUnlocked.Checked = Expression.Value != "false";
+			OnAddedToRow();
+		}
+
+		protected override void BindVariable(string variable)
+		{
+			string[] pieces = variable.Split('.'); ;
+			string propName = "";
+			if (pieces.Length > 1)
+			{
+				propName = pieces[1];
+			}
+			if (!string.IsNullOrEmpty(propName) && propName != "*")
+			{
+				recField.RecordKey = propName;
+			}
+		}
+
+		protected override void OnTargetTypeChanged()
+		{
+			recField.RecordContext = GetTargetCharacter();
+		}
+
+		public override void OnAddedToRow()
+		{
+			OnChangeLabel("Collectible");
+		}
+
+		protected override void AddHandlers()
+		{
+			base.AddHandlers();
+			recField.RecordChanged += RecField_RecordChanged;
+			radLocked.CheckedChanged += RadLocked_CheckedChanged;
+			radUnlocked.CheckedChanged += RadLocked_CheckedChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			base.RemoveHandlers();
+			recField.RecordChanged -= RecField_RecordChanged;
+			radLocked.CheckedChanged -= RadLocked_CheckedChanged;
+			radUnlocked.CheckedChanged -= RadLocked_CheckedChanged;
+		}
+
+		private void RecField_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
+		{
+			Save();	
+		}
+		private void RadLocked_CheckedChanged(object sender, System.EventArgs e)
+		{
+			Save();
+		}
+
+		protected override string GetVariable()
+		{
+			string key = recField.RecordKey;
+			if (string.IsNullOrEmpty(key))
+			{
+				key = "*";
+			}
+
+			return $"collectible.{key}";
+		}
+
+		protected override void OnSave()
+		{
+			base.OnSave();
+			Expression.Operator = "==";
+			if (radLocked.Checked)
+			{
+				Expression.Value = "false";
+			}
+			else
+			{
+				Expression.Value = "true";
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Forms/DialogueResponder.resx b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleControl.resx
similarity index 100%
rename from editor source/SPNATI Character Editor/Forms/DialogueResponder.resx
rename to editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleControl.resx
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleCountControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleCountControl.Designer.cs
similarity index 54%
rename from editor source/SPNATI Character Editor/Controls/EditControls/CollectibleCountControl.Designer.cs
rename to editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleCountControl.Designer.cs
index 2818f8133d6fb69c94b795f4a40ecc79c02b420f..53fabdf7f432df7ed35fe7834db5f387dd8e3a49 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/CollectibleCountControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleCountControl.Designer.cs	
@@ -29,30 +29,35 @@
 		private void InitializeComponent()
 		{
 			this.recField = new Desktop.CommonControls.RecordField();
-			this.label1 = new System.Windows.Forms.Label();
-			this.cboOperator = new System.Windows.Forms.ComboBox();
-			this.valCounter = new System.Windows.Forms.NumericUpDown();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.cboOperator = new Desktop.Skinning.SkinnedComboBox();
+			this.valCounter = new Desktop.Skinning.SkinnedNumericUpDown();
 			((System.ComponentModel.ISupportInitialize)(this.valCounter)).BeginInit();
 			this.SuspendLayout();
 			// 
 			// recField
 			// 
 			this.recField.AllowCreate = false;
-			this.recField.Location = new System.Drawing.Point(63, 1);
+			this.recField.Location = new System.Drawing.Point(206, 1);
 			this.recField.Name = "recField";
 			this.recField.PlaceholderText = null;
 			this.recField.Record = null;
 			this.recField.RecordContext = null;
+			this.recField.RecordFilter = null;
 			this.recField.RecordKey = null;
 			this.recField.RecordType = null;
-			this.recField.Size = new System.Drawing.Size(150, 20);
-			this.recField.TabIndex = 0;
+			this.recField.Size = new System.Drawing.Size(118, 20);
+			this.recField.TabIndex = 1;
 			this.recField.UseAutoComplete = false;
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(3, 4);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(148, 4);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(58, 13);
 			this.label1.TabIndex = 1;
@@ -60,23 +65,27 @@
 			// 
 			// cboOperator
 			// 
+			this.cboOperator.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboOperator.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboOperator.BackColor = System.Drawing.Color.White;
 			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboOperator.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboOperator.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboOperator.FormattingEnabled = true;
-			this.cboOperator.Items.AddRange(new object[] {
-            "==",
-            "<=",
-            "<",
-            ">",
-            ">=",
-            "!="});
-			this.cboOperator.Location = new System.Drawing.Point(219, 0);
+			this.cboOperator.Location = new System.Drawing.Point(327, 0);
 			this.cboOperator.Name = "cboOperator";
-			this.cboOperator.Size = new System.Drawing.Size(39, 21);
+			this.cboOperator.SelectedIndex = -1;
+			this.cboOperator.SelectedItem = null;
+			this.cboOperator.Size = new System.Drawing.Size(44, 21);
+			this.cboOperator.Sorted = false;
 			this.cboOperator.TabIndex = 2;
 			// 
 			// valCounter
 			// 
-			this.valCounter.Location = new System.Drawing.Point(264, 0);
+			this.valCounter.BackColor = System.Drawing.Color.White;
+			this.valCounter.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valCounter.ForeColor = System.Drawing.Color.Black;
+			this.valCounter.Location = new System.Drawing.Point(377, 0);
 			this.valCounter.Name = "valCounter";
 			this.valCounter.Size = new System.Drawing.Size(49, 20);
 			this.valCounter.TabIndex = 3;
@@ -90,7 +99,11 @@
 			this.Controls.Add(this.label1);
 			this.Controls.Add(this.recField);
 			this.Name = "CollectibleCountControl";
-			this.Size = new System.Drawing.Size(433, 21);
+			this.Size = new System.Drawing.Size(745, 21);
+			this.Controls.SetChildIndex(this.recField, 0);
+			this.Controls.SetChildIndex(this.label1, 0);
+			this.Controls.SetChildIndex(this.cboOperator, 0);
+			this.Controls.SetChildIndex(this.valCounter, 0);
 			((System.ComponentModel.ISupportInitialize)(this.valCounter)).EndInit();
 			this.ResumeLayout(false);
 			this.PerformLayout();
@@ -100,8 +113,8 @@
 		#endregion
 
 		private Desktop.CommonControls.RecordField recField;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.ComboBox cboOperator;
-		private System.Windows.Forms.NumericUpDown valCounter;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedComboBox cboOperator;
+		private Desktop.Skinning.SkinnedNumericUpDown valCounter;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleCountControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleCountControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bea2bc364cf3b389b94d89225f7b7db922d1eecb
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleCountControl.cs	
@@ -0,0 +1,105 @@
+using SPNATI_Character_Editor.DataStructures;
+using System.Text.RegularExpressions;
+using System.Collections.Generic;
+using System;
+using SPNATI_Character_Editor.Providers;
+
+namespace SPNATI_Character_Editor
+{
+	[SubVariable("collectible", "counter")]
+	public partial class CollectibleCountControl : PlayerControlBase
+	{
+		public CollectibleCountControl()
+		{
+			InitializeComponent();
+			cboOperator.DataSource = ExpressionTest.Operators;
+			recField.RecordType = typeof(Collectible);
+		}
+
+		protected override void BindVariable(string variable)
+		{
+			string pattern = @"collectible\.([^~]+)\.counter";
+			Regex regex = new Regex(pattern);
+			Match match = regex.Match(variable);
+			if (match.Success)
+			{
+				string propName = match.Groups[1].Value?.ToString();
+				if (!string.IsNullOrEmpty(propName) && propName != "*")
+				{
+					recField.RecordKey = propName;
+					return;
+				}
+			}
+			recField.RecordKey = null;
+		}
+
+		protected override void OnBoundData()
+		{
+			base.OnBoundData();
+			try
+			{
+				cboOperator.SelectedItem = Expression.Operator ?? "==";
+			}
+			catch
+			{
+				cboOperator.SelectedItem = "==";
+			}
+			int count;
+			int.TryParse(Expression.Value, out count);
+			valCounter.Value = Math.Max(valCounter.Minimum, Math.Min(valCounter.Maximum, count));
+			OnAddedToRow();
+		}
+
+		protected override void OnTargetTypeChanged()
+		{
+			recField.RecordContext = GetTargetCharacter();
+		}
+
+		public override void OnAddedToRow()
+		{
+			OnChangeLabel("Collectible (Counter)");
+		}
+
+		protected override void AddHandlers()
+		{
+			base.AddHandlers();
+			recField.RecordChanged += RecField_RecordChanged;
+			cboOperator.SelectedIndexChanged += Field_ValueChanged;
+			valCounter.ValueChanged += Field_ValueChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			base.RemoveHandlers();
+			recField.RecordChanged -= RecField_RecordChanged;
+			cboOperator.SelectedIndexChanged -= Field_ValueChanged;
+			valCounter.ValueChanged -= Field_ValueChanged;
+		}
+
+		private void RecField_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
+		{
+			Save();
+		}
+		private void Field_ValueChanged(object sender, System.EventArgs e)
+		{
+			Save();
+		}
+
+		protected override string GetVariable()
+		{
+			string key = recField.RecordKey;
+			if (string.IsNullOrEmpty(key))
+			{
+				key = "*";
+			}
+			return $"collectible.{key}.counter";
+		}
+
+		protected override void OnSave()
+		{
+			base.OnSave();
+			Expression.Operator = cboOperator.Text;
+			Expression.Value = valCounter.Value.ToString();
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleCountControl.resx b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleCountControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CollectibleCountControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CostumeControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CostumeControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..14d02e113404c21b60ab66ce59243de1223199ac
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CostumeControl.Designer.cs	
@@ -0,0 +1,80 @@
+namespace SPNATI_Character_Editor.Controls.EditControls.VariableControls
+{
+	partial class CostumeControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.chkNot = new Desktop.Skinning.SkinnedCheckBox();
+			this.recCostume = new Desktop.CommonControls.RecordField();
+			this.SuspendLayout();
+			// 
+			// chkNot
+			// 
+			this.chkNot.AutoSize = true;
+			this.chkNot.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkNot.Location = new System.Drawing.Point(285, 2);
+			this.chkNot.Name = "chkNot";
+			this.chkNot.Size = new System.Drawing.Size(43, 17);
+			this.chkNot.TabIndex = 15;
+			this.chkNot.Text = "Not";
+			this.chkNot.UseVisualStyleBackColor = true;
+			// 
+			// recCostume
+			// 
+			this.recCostume.AllowCreate = false;
+			this.recCostume.Location = new System.Drawing.Point(153, 0);
+			this.recCostume.Name = "recCostume";
+			this.recCostume.PlaceholderText = null;
+			this.recCostume.Record = null;
+			this.recCostume.RecordContext = null;
+			this.recCostume.RecordFilter = null;
+			this.recCostume.RecordKey = null;
+			this.recCostume.RecordType = null;
+			this.recCostume.Size = new System.Drawing.Size(126, 20);
+			this.recCostume.TabIndex = 14;
+			this.recCostume.UseAutoComplete = false;
+			// 
+			// CostumeControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.recCostume);
+			this.Controls.Add(this.chkNot);
+			this.Name = "CostumeControl";
+			this.Controls.SetChildIndex(this.chkNot, 0);
+			this.Controls.SetChildIndex(this.recCostume, 0);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedCheckBox chkNot;
+		private Desktop.CommonControls.RecordField recCostume;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CostumeControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CostumeControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5b47d4b015c022639ddab3e4958322996f5fb7ad
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CostumeControl.cs	
@@ -0,0 +1,76 @@
+using Desktop;
+using Desktop.CommonControls;
+
+namespace SPNATI_Character_Editor.Controls.EditControls.VariableControls
+{
+	[SubVariable("costume")]
+	public partial class CostumeControl : PlayerControlBase
+	{
+		public CostumeControl()
+		{
+			InitializeComponent();
+			recCostume.RecordType = typeof(Costume);
+			recCostume.RecordFilter = FilterCostume;
+		}
+
+		private bool FilterCostume(IRecord record)
+		{
+			Character character = GetTargetCharacter();
+			if (character == null)
+			{
+				return true;
+			}
+			Costume costume = record as Costume;
+			return costume.Character == character || costume.Key == "default";
+		}
+
+		protected override void OnBoundData()
+		{
+			base.OnBoundData();
+			chkNot.Checked = (Expression.Operator == "!=");
+			recCostume.RecordKey = Expression.Value;
+			OnAddedToRow();
+		}
+
+		public override void OnAddedToRow()
+		{
+			OnChangeLabel("Costume");
+		}
+
+		protected override void AddHandlers()
+		{
+			base.AddHandlers();
+			chkNot.CheckedChanged += ValueChanged;
+			recCostume.RecordChanged += RecCostume_RecordChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			base.RemoveHandlers();
+			chkNot.CheckedChanged -= ValueChanged;
+			recCostume.RecordChanged -= RecCostume_RecordChanged;
+		}
+
+		private void RecCostume_RecordChanged(object sender, RecordEventArgs e)
+		{
+			Save();
+		}
+
+		private void ValueChanged(object sender, System.EventArgs e)
+		{
+			Save();
+		}
+
+		protected override void OnSave()
+		{
+			base.OnSave();
+			Expression.Operator = (chkNot.Checked ? "!=" : "==");
+			Expression.Value = recCostume.RecordKey;
+		}
+
+		protected override string GetVariable()
+		{
+			return "costume";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CostumeControl.resx b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CostumeControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/CostumeControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PersistentMarkerControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PersistentMarkerControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..495e8e8a7dcb3561011af8e07cb56a632e8954e4
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PersistentMarkerControl.Designer.cs	
@@ -0,0 +1,118 @@
+namespace SPNATI_Character_Editor
+{
+	partial class PersistentMarkerControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.recField = new Desktop.CommonControls.RecordField();
+			this.txtValue = new Desktop.Skinning.SkinnedTextBox();
+			this.cboOperator = new Desktop.Skinning.SkinnedComboBox();
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.SuspendLayout();
+			// 
+			// recField
+			// 
+			this.recField.AllowCreate = true;
+			this.recField.Location = new System.Drawing.Point(192, 1);
+			this.recField.Name = "recField";
+			this.recField.PlaceholderText = null;
+			this.recField.Record = null;
+			this.recField.RecordContext = null;
+			this.recField.RecordFilter = null;
+			this.recField.RecordKey = null;
+			this.recField.RecordType = null;
+			this.recField.Size = new System.Drawing.Size(98, 20);
+			this.recField.TabIndex = 1;
+			this.recField.UseAutoComplete = false;
+			// 
+			// txtValue
+			// 
+			this.txtValue.BackColor = System.Drawing.Color.White;
+			this.txtValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtValue.ForeColor = System.Drawing.Color.Black;
+			this.txtValue.Location = new System.Drawing.Point(358, 0);
+			this.txtValue.Name = "txtValue";
+			this.txtValue.Size = new System.Drawing.Size(78, 20);
+			this.txtValue.TabIndex = 3;
+			// 
+			// cboOperator
+			// 
+			this.cboOperator.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboOperator.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboOperator.BackColor = System.Drawing.Color.White;
+			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboOperator.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboOperator.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboOperator.FormattingEnabled = true;
+			this.cboOperator.Location = new System.Drawing.Point(296, 0);
+			this.cboOperator.Name = "cboOperator";
+			this.cboOperator.SelectedIndex = -1;
+			this.cboOperator.SelectedItem = null;
+			this.cboOperator.Size = new System.Drawing.Size(56, 21);
+			this.cboOperator.Sorted = false;
+			this.cboOperator.TabIndex = 2;
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.AutoSize = true;
+			this.skinnedLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel1.Location = new System.Drawing.Point(148, 3);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(43, 13);
+			this.skinnedLabel1.TabIndex = 13;
+			this.skinnedLabel1.Text = "Marker:";
+			// 
+			// PersistentMarkerControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.skinnedLabel1);
+			this.Controls.Add(this.txtValue);
+			this.Controls.Add(this.cboOperator);
+			this.Controls.Add(this.recField);
+			this.Name = "PersistentMarkerControl";
+			this.Size = new System.Drawing.Size(605, 21);
+			this.Controls.SetChildIndex(this.recField, 0);
+			this.Controls.SetChildIndex(this.cboOperator, 0);
+			this.Controls.SetChildIndex(this.txtValue, 0);
+			this.Controls.SetChildIndex(this.skinnedLabel1, 0);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.CommonControls.RecordField recField;
+		private Desktop.Skinning.SkinnedTextBox txtValue;
+		private Desktop.Skinning.SkinnedComboBox cboOperator;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PersistentMarkerControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PersistentMarkerControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1b7a501d2c02c9339fff5586370ca58567409ed3
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PersistentMarkerControl.cs	
@@ -0,0 +1,147 @@
+using Desktop;
+using Desktop.CommonControls;
+using System;
+
+namespace SPNATI_Character_Editor
+{
+	[SubVariable("persistent")]
+	[SubVariable("marker")]
+	public partial class PersistentMarkerControl : PlayerControlBase
+	{
+		public bool Persistent;
+
+		public PersistentMarkerControl()
+		{
+			InitializeComponent();
+			cboOperator.DataSource = ExpressionTest.Operators;
+
+			recField.RecordType = typeof(Marker);
+		}
+
+		public override void OnAddedToRow()
+		{
+			string label = "Marker";
+			if (Persistent)
+			{
+				label += " (Persistent)";
+			}
+			OnChangeLabel(label);
+		}
+
+		private bool FilterPrivateMarkers(IRecord record)
+		{
+			Marker marker = record as Marker;
+			return marker.Scope == MarkerScope.Public;
+		}
+
+		protected override void OnBoundData()
+		{
+			base.OnBoundData();
+			cboOperator.Text = Expression.Operator;
+			txtValue.Text = Expression.Value;
+			OnAddedToRow();
+		}
+
+		protected override void BindVariable(string variable)
+		{
+			string[] pieces = variable.Split('.'); ;
+			string varName = pieces[0];
+			Persistent = varName == "persistent";
+			string propName = "";
+			if (pieces.Length > 1)
+			{
+				propName = pieces[1];
+			}
+			if (!string.IsNullOrEmpty(propName) && propName != "*")
+			{
+				recField.RecordKey = propName;
+			}
+		}
+
+		protected override void OnTargetTypeChanged()
+		{
+			recField.RecordContext = GetTargetCharacter();
+			TargetId targetType = TargetType;
+			if (targetType?.Key == "self")
+			{
+				recField.RecordFilter = null;
+			}
+			else
+			{
+				recField.RecordFilter = FilterPrivateMarkers;
+			}
+		}
+		
+		protected override void RemoveHandlers()
+		{
+			base.RemoveHandlers();
+			recField.RecordChanged -= RecordChanged;
+			cboOperator.SelectedIndexChanged -= ValueChanged;
+			txtValue.TextChanged -= ValueChanged;
+		}
+
+		protected override void AddHandlers()
+		{
+			base.AddHandlers();
+			recField.RecordChanged += RecordChanged;
+			cboOperator.SelectedIndexChanged += ValueChanged;
+			txtValue.TextChanged += ValueChanged;
+		}
+
+		protected override void OnClear()
+		{
+			RemoveHandlers();
+			recField.RecordKey = null;
+			cboOperator.SelectedIndex = 0;
+			txtValue.Text = "";
+			AddHandlers();
+			Save();
+		}
+
+		protected override void OnSave()
+		{
+			base.OnSave();
+			string op = cboOperator.SelectedItem?.ToString();
+			if (string.IsNullOrEmpty(op))
+			{
+				op = "==";
+			}
+			Expression.Operator = op;
+
+			string value = txtValue.Text;
+			if (string.IsNullOrEmpty(value))
+			{
+				value = null;
+			}
+			Expression.Value = value;
+		}
+
+		protected override string GetVariable()
+		{
+			string key = recField.RecordKey;
+			if (string.IsNullOrEmpty(key))
+			{
+				key = "*";
+			}
+
+			if (Persistent)
+			{
+				return $"persistent.{key}";
+			}
+			else
+			{
+				return $"marker.{key}";
+			}
+		}
+
+		private void ValueChanged(object sender, EventArgs e)
+		{
+			Save();
+		}
+
+		private void RecordChanged(object sender, RecordEventArgs record)
+		{
+			Save();
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PersistentMarkerControl.resx b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PersistentMarkerControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PersistentMarkerControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerControlBase.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerControlBase.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..987f925e412d5f0e02d20150da67c9d6f6b257b0
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerControlBase.Designer.cs	
@@ -0,0 +1,81 @@
+namespace SPNATI_Character_Editor
+{
+	partial class PlayerControlBase
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.recType = new Desktop.CommonControls.RecordField();
+			this.SuspendLayout();
+			// 
+			// label2
+			// 
+			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label2.Location = new System.Drawing.Point(3, 3);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(39, 13);
+			this.label2.TabIndex = 12;
+			this.label2.Text = "Player:";
+			// 
+			// recType
+			// 
+			this.recType.AllowCreate = false;
+			this.recType.Location = new System.Drawing.Point(48, 0);
+			this.recType.Name = "recType";
+			this.recType.PlaceholderText = null;
+			this.recType.Record = null;
+			this.recType.RecordContext = null;
+			this.recType.RecordFilter = null;
+			this.recType.RecordKey = null;
+			this.recType.RecordType = null;
+			this.recType.Size = new System.Drawing.Size(100, 20);
+			this.recType.TabIndex = 0;
+			this.recType.UseAutoComplete = false;
+			// 
+			// PlayerControlBase
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.label2);
+			this.Controls.Add(this.recType);
+			this.Name = "PlayerControlBase";
+			this.Size = new System.Drawing.Size(527, 21);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.CommonControls.RecordField recType;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerControlBase.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerControlBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..32affd68fe934d093d589b91abde58a059bc4f09
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerControlBase.cs	
@@ -0,0 +1,120 @@
+using Desktop;
+
+namespace SPNATI_Character_Editor
+{
+	public partial class PlayerControlBase : SubVariableControl
+	{
+		public PlayerControlBase()
+		{
+			InitializeComponent();
+			recType.RecordType = typeof(TargetId);
+			Bindings.Add("AlsoPlaying");
+			Bindings.Add("Target");
+		}
+
+		public TargetId TargetType
+		{
+			get { return recType.Record as TargetId; }
+		}
+
+		public ExpressionTest Expression { get; private set; }
+
+		protected override void OnBindingUpdated(string property)
+		{
+			if (property == "AlsoPlaying" && (recType.RecordKey == "_" || recType.RecordKey == null))
+			{
+				string targetType;
+				string variable;
+				ExtractExpressionPieces(out targetType, out variable);
+				recType.RecordKey = targetType;
+			}
+			else if (property == "Target" && recType.RecordKey == "target")
+			{
+				OnTargetTypeChanged();
+			}
+		}
+
+		protected override void AddHandlers()
+		{
+			recType.RecordChanged += RecType_RecordChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			recType.RecordChanged -= RecType_RecordChanged;
+		}
+
+		private void RecType_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
+		{
+			OnTargetTypeChanged();
+			Save();
+		}
+
+		protected virtual void OnTargetTypeChanged() { }
+
+		protected Character GetTargetCharacter()
+		{
+			string targetType = recType.RecordKey;
+			if (targetType == "self")
+			{
+				return Context as Character;
+			}
+			else if (targetType == "target")
+			{
+				Case dataCase = Data as Case;
+				if (!string.IsNullOrEmpty(dataCase.Target))
+				{
+					return CharacterDatabase.Get(dataCase.Target);
+				}
+			}
+			return CharacterDatabase.GetById(targetType);
+		}
+
+		private bool FilterTarget(IRecord record)
+		{
+			if (record.Key != "target") { return true; }
+			Case workingCase = Data as Case;
+			if (workingCase == null)
+			{
+				return true;
+			}
+			Trigger trigger = TriggerDatabase.GetTrigger(workingCase.Tag);
+			return trigger.HasTarget;
+
+		}
+
+		protected override void OnBoundData()
+		{
+			recType.RecordContext = Data;
+			Expression = GetValue() as ExpressionTest;
+			string variable;
+			string targetType;
+			ExtractExpressionPieces(out targetType, out variable);
+
+			recType.RecordFilter = FilterTarget;
+			recType.RecordKey = targetType;
+			OnTargetTypeChanged();
+			BindVariable(variable);
+		}
+
+		protected virtual void BindVariable(string variable)
+		{
+		}
+
+		protected override void OnSave()
+		{
+			string targetType = recType.RecordKey;
+			if (string.IsNullOrEmpty(targetType))
+			{
+				targetType = "_";
+			}
+			string v = GetVariable();
+			Expression.Expression = $"~{targetType}.{v}~";
+		}
+
+		protected virtual string GetVariable()
+		{
+			return "";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerControlBase.resx b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerControlBase.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerControlBase.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerStageControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerStageControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e854c493570f5be3574481eae669082dd7dab453
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerStageControl.Designer.cs	
@@ -0,0 +1,87 @@
+namespace SPNATI_Character_Editor.Controls.EditControls.VariableControls
+{
+	partial class PlayerStageControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.cboOperator = new Desktop.Skinning.SkinnedComboBox();
+			this.cboStage = new Desktop.Skinning.SkinnedComboBox();
+			this.SuspendLayout();
+			// 
+			// cboOperator
+			// 
+			this.cboOperator.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboOperator.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboOperator.BackColor = System.Drawing.Color.White;
+			this.cboOperator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboOperator.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboOperator.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboOperator.FormattingEnabled = true;
+			this.cboOperator.Location = new System.Drawing.Point(151, 0);
+			this.cboOperator.Name = "cboOperator";
+			this.cboOperator.SelectedIndex = -1;
+			this.cboOperator.SelectedItem = null;
+			this.cboOperator.Size = new System.Drawing.Size(96, 21);
+			this.cboOperator.Sorted = false;
+			this.cboOperator.TabIndex = 13;
+			// 
+			// cboStage
+			// 
+			this.cboStage.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
+			this.cboStage.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
+			this.cboStage.BackColor = System.Drawing.Color.White;
+			this.cboStage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.Simple;
+			this.cboStage.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboStage.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboStage.Location = new System.Drawing.Point(250, 0);
+			this.cboStage.Name = "cboStage";
+			this.cboStage.SelectedIndex = -1;
+			this.cboStage.SelectedItem = null;
+			this.cboStage.Size = new System.Drawing.Size(142, 21);
+			this.cboStage.Sorted = false;
+			this.cboStage.TabIndex = 15;
+			// 
+			// PlayerStageControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.cboStage);
+			this.Controls.Add(this.cboOperator);
+			this.Name = "PlayerStageControl";
+			this.Controls.SetChildIndex(this.cboOperator, 0);
+			this.Controls.SetChildIndex(this.cboStage, 0);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedComboBox cboOperator;
+		private Desktop.Skinning.SkinnedComboBox cboStage;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerStageControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerStageControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1a53ad5538c2027420320878c4e8e3aea38f0cb9
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerStageControl.cs	
@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+
+namespace SPNATI_Character_Editor.Controls.EditControls.VariableControls
+{
+	[SubVariable("stage")]
+	public partial class PlayerStageControl : PlayerControlBase
+	{
+		private static readonly KeyValuePair<string, string>[] Operators = {
+			new KeyValuePair<string, string>(null, ""),
+			new KeyValuePair<string, string>("==", "is at"),
+			new KeyValuePair<string, string>("!=", "is not at"),
+			new KeyValuePair<string, string>("<=", "is at or before"),
+			new KeyValuePair<string, string>("<", "is before"),
+			new KeyValuePair<string, string>(">=", "is at or after"),
+			new KeyValuePair<string, string>(">", "is after"),
+		};
+
+		public PlayerStageControl()
+		{
+			InitializeComponent();
+			cboOperator.DisplayMember = "Value";
+			cboOperator.ValueMember = "Key";
+			cboOperator.DataSource = Operators;
+		}
+
+		protected override string GetVariable()
+		{
+			return "stage";
+		}
+
+		protected override void OnBoundData()
+		{
+			base.OnBoundData();
+			cboOperator.SelectedValue = Expression.Operator ?? "==";
+
+			int stage;
+			if (int.TryParse(Expression.Value, out stage) && stage >= 0 && stage < cboStage.Items.Count)
+			{
+				cboStage.SelectedIndex = stage;
+			}
+			else
+			{
+				cboStage.SelectedIndex = -1;
+			}
+			OnAddedToRow();
+		}
+
+		protected override void OnTargetTypeChanged()
+		{
+			UpdateStage();
+		}
+
+		private void UpdateStage()
+		{
+			Character character = GetTargetCharacter();
+			int stage = cboStage.SelectedIndex;
+			List<object> data = new List<object>();
+
+			if (character == null)
+			{
+				//If the character is not valid, still allow something but there's no way to give a useful name to it
+				for (int i = 0; i < 8 + Clothing.ExtraStages; i++)
+				{
+					data.Add(i);
+				}
+				cboStage.DataSource = data;
+			}
+			else
+			{
+				IWardrobe skin = character;
+				for (int i = 0; i < character.Layers + Clothing.ExtraStages; i++)
+				{
+					data.Add(character.LayerToStageName(i, false, skin));
+				}
+				cboStage.DataSource = data;
+			}
+			if (stage >= 0 && cboStage.Items.Count > stage)
+			{
+				cboStage.SelectedIndex = stage;
+				cboStage.Text = cboStage.Items[stage].ToString();
+			}
+		}
+
+		public override void OnAddedToRow()
+		{
+			OnChangeLabel("Stage");
+		}
+
+		protected override void AddHandlers()
+		{
+			base.AddHandlers();
+			cboOperator.SelectedIndexChanged += CboOperator_ValueChanged;
+			cboStage.SelectedIndexChanged += CboOperator_ValueChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			base.AddHandlers();
+			cboStage.SelectedIndexChanged -= CboOperator_ValueChanged;
+			cboOperator.SelectedIndexChanged -= CboOperator_ValueChanged;
+		}
+
+		private void CboOperator_ValueChanged(object sender, System.EventArgs e)
+		{
+			Save();
+		}
+
+		protected override void OnSave()
+		{
+			base.OnSave();
+			Expression.Operator = cboOperator.SelectedValue?.ToString() ?? cboOperator.Text;
+			Expression.Value = cboStage.SelectedIndex >= 0 ? cboStage.SelectedIndex.ToString() : "";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerStageControl.resx b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerStageControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/PlayerStageControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/TagControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/TagControl.Designer.cs
similarity index 66%
rename from editor source/SPNATI Character Editor/Controls/EditControls/TagControl.Designer.cs
rename to editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/TagControl.Designer.cs
index 1f909d9dd8d152026d88eb7c747b6cb15e9d57cc..6b42425362ba92d65b22eb9733666dff296cae61 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/TagControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/TagControl.Designer.cs	
@@ -29,28 +29,33 @@
 		private void InitializeComponent()
 		{
 			this.recField = new Desktop.CommonControls.RecordField();
-			this.label1 = new System.Windows.Forms.Label();
-			this.chkNot = new System.Windows.Forms.CheckBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.chkNot = new Desktop.Skinning.SkinnedCheckBox();
 			this.SuspendLayout();
 			// 
 			// recField
 			// 
 			this.recField.AllowCreate = false;
-			this.recField.Location = new System.Drawing.Point(63, 1);
+			this.recField.Location = new System.Drawing.Point(198, 1);
 			this.recField.Name = "recField";
 			this.recField.PlaceholderText = null;
 			this.recField.Record = null;
 			this.recField.RecordContext = null;
+			this.recField.RecordFilter = null;
 			this.recField.RecordKey = null;
 			this.recField.RecordType = null;
-			this.recField.Size = new System.Drawing.Size(150, 20);
-			this.recField.TabIndex = 0;
+			this.recField.Size = new System.Drawing.Size(117, 20);
+			this.recField.TabIndex = 1;
 			this.recField.UseAutoComplete = false;
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(3, 4);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(148, 4);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(47, 13);
 			this.label1.TabIndex = 1;
@@ -59,7 +64,8 @@
 			// chkNot
 			// 
 			this.chkNot.AutoSize = true;
-			this.chkNot.Location = new System.Drawing.Point(219, 3);
+			this.chkNot.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkNot.Location = new System.Drawing.Point(321, 3);
 			this.chkNot.Name = "chkNot";
 			this.chkNot.Size = new System.Drawing.Size(43, 17);
 			this.chkNot.TabIndex = 2;
@@ -75,6 +81,9 @@
 			this.Controls.Add(this.recField);
 			this.Name = "TagControl";
 			this.Size = new System.Drawing.Size(433, 21);
+			this.Controls.SetChildIndex(this.recField, 0);
+			this.Controls.SetChildIndex(this.label1, 0);
+			this.Controls.SetChildIndex(this.chkNot, 0);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -83,7 +92,7 @@
 		#endregion
 
 		private Desktop.CommonControls.RecordField recField;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.CheckBox chkNot;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedCheckBox chkNot;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/TagControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/TagControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7f180be8bd4844bddd2d4e9d94e747202d9311e1
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/TagControl.cs	
@@ -0,0 +1,92 @@
+namespace SPNATI_Character_Editor
+{
+	[SubVariable("tag")]
+	public partial class TagControl : PlayerControlBase
+	{
+		public TagControl()
+		{
+			InitializeComponent();
+
+			recField.RecordType = typeof(Tag);
+		}
+
+		protected override void OnBoundData()
+		{
+			base.OnBoundData();
+			chkNot.Checked = Expression.Value == "false";
+			OnAddedToRow();
+		}
+
+		protected override void BindVariable(string variable)
+		{
+			string[] pieces = variable.Split('.'); ;
+			string propName = "";
+			if (pieces.Length > 1)
+			{
+				propName = pieces[1];
+			}
+			if (!string.IsNullOrEmpty(propName) && propName != "*")
+			{
+				recField.RecordKey = propName;
+			}
+		}
+
+		protected override void OnTargetTypeChanged()
+		{
+			recField.RecordContext = GetTargetCharacter();
+		}
+
+		public override void OnAddedToRow()
+		{
+			OnChangeLabel("Tag");
+		}
+
+		protected override void AddHandlers()
+		{
+			base.AddHandlers();
+			recField.RecordChanged += RecField_RecordChanged;
+			chkNot.CheckedChanged += Field_CheckedChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			base.RemoveHandlers();
+			recField.RecordChanged -= RecField_RecordChanged;
+			chkNot.CheckedChanged -= Field_CheckedChanged;
+		}
+
+		private void RecField_RecordChanged(object sender, Desktop.CommonControls.RecordEventArgs e)
+		{
+			Save();	
+		}
+		private void Field_CheckedChanged(object sender, System.EventArgs e)
+		{
+			Save();
+		}
+
+		protected override string GetVariable()
+		{
+			string key = recField.RecordKey;
+			if (string.IsNullOrEmpty(key))
+			{
+				key = "*";
+			}
+
+			return $"tag.{key}";
+		}
+
+		protected override void OnSave()
+		{
+			base.OnSave();
+			Expression.Operator = "==";
+			if (chkNot.Checked)
+			{
+				Expression.Value = "false";
+			}
+			else
+			{
+				Expression.Value = "true";
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/TagControl.resx b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/TagControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VariableControls/TagControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VerticalAlignmentControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VerticalAlignmentControl.Designer.cs
index df9c74512047f431ca652e8e1aefaa9fd508f442..f0d2d67373dd5cf2d10e21cf4b21e2d9e27fe9f2 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/VerticalAlignmentControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VerticalAlignmentControl.Designer.cs	
@@ -29,9 +29,9 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			this.chkTop = new System.Windows.Forms.RadioButton();
-			this.chkMiddle = new System.Windows.Forms.RadioButton();
-			this.chkBottom = new System.Windows.Forms.RadioButton();
+			this.chkTop = new Desktop.Skinning.SkinnedRadioButton();
+			this.chkMiddle = new Desktop.Skinning.SkinnedRadioButton();
+			this.chkBottom = new Desktop.Skinning.SkinnedRadioButton();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
 			this.SuspendLayout();
 			// 
@@ -86,9 +86,9 @@
 		}
 
 		#endregion
-		private System.Windows.Forms.RadioButton chkTop;
-		private System.Windows.Forms.RadioButton chkMiddle;
-		private System.Windows.Forms.RadioButton chkBottom;
+		private Desktop.Skinning.SkinnedRadioButton chkTop;
+		private Desktop.Skinning.SkinnedRadioButton chkMiddle;
+		private Desktop.Skinning.SkinnedRadioButton chkBottom;
 		private System.Windows.Forms.ToolTip toolTip1;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EditControls/VerticalAlignmentControl.cs b/editor source/SPNATI Character Editor/Controls/EditControls/VerticalAlignmentControl.cs
index 6aab9140ef11b2d889c5713a020d4d49f11ee286..0a94620ced97e28cfab04e79458b36875c668958 100644
--- a/editor source/SPNATI Character Editor/Controls/EditControls/VerticalAlignmentControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EditControls/VerticalAlignmentControl.cs	
@@ -47,7 +47,7 @@ namespace SPNATI_Character_Editor.Controls.EditControls
 			Save();
 		}
 
-		public override void Clear()
+		protected override void OnClear()
 		{
 			RemoveHandlers();
 			chkTop.Checked = false;
@@ -56,7 +56,7 @@ namespace SPNATI_Character_Editor.Controls.EditControls
 			AddHandlers();
 		}
 
-		public override void Save()
+		protected override void OnSave()
 		{
 			if (chkTop.Checked)
 			{
diff --git a/editor source/SPNATI Character Editor/Controls/EpilogueEditor.Designer.cs b/editor source/SPNATI Character Editor/Controls/EpilogueEditor.Designer.cs
index c0753ad3c2e53a4c029201a0a00f8c24db26cfe3..82d0c1579f83186001f9811a350d83820b30c0ff 100644
--- a/editor source/SPNATI Character Editor/Controls/EpilogueEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EpilogueEditor.Designer.cs	
@@ -29,54 +29,63 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			this.cboEnding = new System.Windows.Forms.ComboBox();
-			this.label1 = new System.Windows.Forms.Label();
+			this.cboEnding = new Desktop.Skinning.SkinnedComboBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
 			this.imageFileDialog = new System.Windows.Forms.OpenFileDialog();
-			this.cmdDeleteEnding = new System.Windows.Forms.Button();
-			this.cmdAddEnding = new System.Windows.Forms.Button();
+			this.cmdDeleteEnding = new Desktop.Skinning.SkinnedButton();
+			this.cmdAddEnding = new Desktop.Skinning.SkinnedButton();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
-			this.pnlHeader = new System.Windows.Forms.Panel();
+			this.pnlHeader = new Desktop.Skinning.SkinnedPanel();
 			this.tmrActivate = new System.Windows.Forms.Timer(this.components);
-			this.tabs = new System.Windows.Forms.TabControl();
+			this.tabs = new Desktop.Skinning.SkinnedTabControl();
 			this.pageGeneral = new System.Windows.Forms.TabPage();
-			this.grpConditions = new System.Windows.Forms.GroupBox();
+			this.grpConditions = new Desktop.Skinning.SkinnedGroupBox();
 			this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
-			this.groupBox1 = new System.Windows.Forms.GroupBox();
-			this.label3 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
-			this.label7 = new System.Windows.Forms.Label();
-			this.groupBox2 = new System.Windows.Forms.GroupBox();
-			this.label4 = new System.Windows.Forms.Label();
-			this.label5 = new System.Windows.Forms.Label();
-			this.label6 = new System.Windows.Forms.Label();
-			this.grpInfo = new System.Windows.Forms.GroupBox();
-			this.tableGeneral = new Desktop.CommonControls.PropertyTable();
-			this.pageScenes = new System.Windows.Forms.TabPage();
-			this.selAnyMarkers = new SPNATI_Character_Editor.Controls.SelectBox();
-			this.selNotMarkers = new SPNATI_Character_Editor.Controls.SelectBox();
-			this.selAllMarkers = new SPNATI_Character_Editor.Controls.SelectBox();
+			this.groupBox2 = new Desktop.Skinning.SkinnedGroupBox();
 			this.selAlsoPlayingAnyMarkers = new SPNATI_Character_Editor.Controls.SelectBox();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
 			this.selAlsoPlayingNotMarkers = new SPNATI_Character_Editor.Controls.SelectBox();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
 			this.selAlsoPlayingAllMarkers = new SPNATI_Character_Editor.Controls.SelectBox();
+			this.label6 = new Desktop.Skinning.SkinnedLabel();
+			this.groupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.selAnyMarkers = new SPNATI_Character_Editor.Controls.SelectBox();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.selNotMarkers = new SPNATI_Character_Editor.Controls.SelectBox();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.selAllMarkers = new SPNATI_Character_Editor.Controls.SelectBox();
+			this.label7 = new Desktop.Skinning.SkinnedLabel();
+			this.grpInfo = new Desktop.Skinning.SkinnedGroupBox();
+			this.tableGeneral = new Desktop.CommonControls.PropertyTable();
+			this.pageScenes = new System.Windows.Forms.TabPage();
 			this.canvas = new SPNATI_Character_Editor.Controls.EpilogueCanvas();
+			this.strip = new Desktop.Skinning.SkinnedTabStrip();
 			this.pnlHeader.SuspendLayout();
 			this.tabs.SuspendLayout();
 			this.pageGeneral.SuspendLayout();
 			this.grpConditions.SuspendLayout();
 			this.tableLayoutPanel1.SuspendLayout();
-			this.groupBox1.SuspendLayout();
 			this.groupBox2.SuspendLayout();
+			this.groupBox1.SuspendLayout();
 			this.grpInfo.SuspendLayout();
 			this.pageScenes.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// cboEnding
 			// 
+			this.cboEnding.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboEnding.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboEnding.BackColor = System.Drawing.Color.White;
 			this.cboEnding.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboEnding.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cboEnding.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.cboEnding.FormattingEnabled = true;
 			this.cboEnding.Location = new System.Drawing.Point(49, 8);
 			this.cboEnding.Name = "cboEnding";
+			this.cboEnding.SelectedIndex = -1;
+			this.cboEnding.SelectedItem = null;
 			this.cboEnding.Size = new System.Drawing.Size(170, 21);
+			this.cboEnding.Sorted = false;
 			this.cboEnding.TabIndex = 0;
 			this.toolTip1.SetToolTip(this.cboEnding, "Select an ending to edit");
 			this.cboEnding.SelectedIndexChanged += new System.EventHandler(this.cboEnding_SelectedIndexChanged);
@@ -84,6 +93,10 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.White;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Primary;
 			this.label1.Location = new System.Drawing.Point(3, 11);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(43, 13);
@@ -97,18 +110,24 @@
 			// cmdDeleteEnding
 			// 
 			this.cmdDeleteEnding.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdDeleteEnding.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdDeleteEnding.Enabled = false;
-			this.cmdDeleteEnding.Location = new System.Drawing.Point(866, 7);
+			this.cmdDeleteEnding.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdDeleteEnding.Flat = false;
+			this.cmdDeleteEnding.Location = new System.Drawing.Point(892, 7);
 			this.cmdDeleteEnding.Name = "cmdDeleteEnding";
-			this.cmdDeleteEnding.Size = new System.Drawing.Size(101, 23);
+			this.cmdDeleteEnding.Size = new System.Drawing.Size(75, 23);
 			this.cmdDeleteEnding.TabIndex = 20;
-			this.cmdDeleteEnding.Text = "Delete Ending";
+			this.cmdDeleteEnding.Text = "Delete";
 			this.toolTip1.SetToolTip(this.cmdDeleteEnding, "Delete this ending");
 			this.cmdDeleteEnding.UseVisualStyleBackColor = true;
 			this.cmdDeleteEnding.Click += new System.EventHandler(this.cmdDeleteEnding_Click);
 			// 
 			// cmdAddEnding
 			// 
+			this.cmdAddEnding.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdAddEnding.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdAddEnding.Flat = false;
 			this.cmdAddEnding.Location = new System.Drawing.Point(225, 7);
 			this.cmdAddEnding.Name = "cmdAddEnding";
 			this.cmdAddEnding.Size = new System.Drawing.Size(75, 23);
@@ -127,8 +146,10 @@
 			this.pnlHeader.Dock = System.Windows.Forms.DockStyle.Top;
 			this.pnlHeader.Location = new System.Drawing.Point(0, 0);
 			this.pnlHeader.Name = "pnlHeader";
+			this.pnlHeader.PanelType = Desktop.Skinning.SkinnedBackgroundType.Primary;
 			this.pnlHeader.Size = new System.Drawing.Size(973, 36);
 			this.pnlHeader.TabIndex = 24;
+			this.pnlHeader.TabSide = Desktop.Skinning.TabSide.None;
 			// 
 			// tmrActivate
 			// 
@@ -137,95 +158,59 @@
 			// 
 			// tabs
 			// 
+			this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
 			this.tabs.Controls.Add(this.pageGeneral);
 			this.tabs.Controls.Add(this.pageScenes);
-			this.tabs.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.tabs.Enabled = false;
-			this.tabs.Location = new System.Drawing.Point(0, 36);
+			this.tabs.Location = new System.Drawing.Point(0, 57);
 			this.tabs.Name = "tabs";
 			this.tabs.SelectedIndex = 0;
-			this.tabs.Size = new System.Drawing.Size(973, 610);
+			this.tabs.Size = new System.Drawing.Size(973, 589);
 			this.tabs.TabIndex = 25;
 			// 
 			// pageGeneral
 			// 
+			this.pageGeneral.BackColor = System.Drawing.Color.White;
 			this.pageGeneral.Controls.Add(this.grpConditions);
 			this.pageGeneral.Controls.Add(this.grpInfo);
 			this.pageGeneral.Location = new System.Drawing.Point(4, 22);
 			this.pageGeneral.Name = "pageGeneral";
 			this.pageGeneral.Padding = new System.Windows.Forms.Padding(3);
-			this.pageGeneral.Size = new System.Drawing.Size(965, 584);
+			this.pageGeneral.Size = new System.Drawing.Size(965, 563);
 			this.pageGeneral.TabIndex = 0;
 			this.pageGeneral.Text = "General";
-			this.pageGeneral.UseVisualStyleBackColor = true;
 			// 
 			// grpConditions
 			// 
+			this.grpConditions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
 			this.grpConditions.Controls.Add(this.tableLayoutPanel1);
 			this.grpConditions.Location = new System.Drawing.Point(374, 6);
 			this.grpConditions.Name = "grpConditions";
-			this.grpConditions.Size = new System.Drawing.Size(585, 572);
+			this.grpConditions.Size = new System.Drawing.Size(585, 551);
 			this.grpConditions.TabIndex = 2;
 			this.grpConditions.TabStop = false;
 			this.grpConditions.Text = "Unlock Conditions";
 			// 
 			// tableLayoutPanel1
 			// 
+			this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
 			this.tableLayoutPanel1.ColumnCount = 2;
 			this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
 			this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
-			this.tableLayoutPanel1.Controls.Add(this.groupBox1, 0, 0);
 			this.tableLayoutPanel1.Controls.Add(this.groupBox2, 1, 0);
-			this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
-			this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 16);
+			this.tableLayoutPanel1.Controls.Add(this.groupBox1, 0, 0);
+			this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 22);
 			this.tableLayoutPanel1.Name = "tableLayoutPanel1";
 			this.tableLayoutPanel1.RowCount = 1;
 			this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
-			this.tableLayoutPanel1.Size = new System.Drawing.Size(579, 553);
+			this.tableLayoutPanel1.Size = new System.Drawing.Size(579, 523);
 			this.tableLayoutPanel1.TabIndex = 46;
 			// 
-			// groupBox1
-			// 
-			this.groupBox1.Controls.Add(this.selAnyMarkers);
-			this.groupBox1.Controls.Add(this.label3);
-			this.groupBox1.Controls.Add(this.selNotMarkers);
-			this.groupBox1.Controls.Add(this.label2);
-			this.groupBox1.Controls.Add(this.selAllMarkers);
-			this.groupBox1.Controls.Add(this.label7);
-			this.groupBox1.Location = new System.Drawing.Point(3, 3);
-			this.groupBox1.Name = "groupBox1";
-			this.groupBox1.Size = new System.Drawing.Size(283, 360);
-			this.groupBox1.TabIndex = 44;
-			this.groupBox1.TabStop = false;
-			this.groupBox1.Text = "Own markers";
-			// 
-			// label3
-			// 
-			this.label3.AutoSize = true;
-			this.label3.Location = new System.Drawing.Point(10, 245);
-			this.label3.Name = "label3";
-			this.label3.Size = new System.Drawing.Size(225, 13);
-			this.label3.TabIndex = 0;
-			this.label3.Text = "Require any of the following markers to be set:";
-			// 
-			// label2
-			// 
-			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(10, 132);
-			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(232, 13);
-			this.label2.TabIndex = 0;
-			this.label2.Text = "Require none of the following markers to be set:";
-			// 
-			// label7
-			// 
-			this.label7.AutoSize = true;
-			this.label7.Location = new System.Drawing.Point(7, 20);
-			this.label7.Name = "label7";
-			this.label7.Size = new System.Drawing.Size(206, 13);
-			this.label7.TabIndex = 0;
-			this.label7.Text = "Require all the following markers to be set:";
-			// 
 			// groupBox2
 			// 
 			this.groupBox2.Controls.Add(this.selAlsoPlayingAnyMarkers);
@@ -241,150 +226,246 @@
 			this.groupBox2.TabStop = false;
 			this.groupBox2.Text = "Other markers";
 			// 
+			// selAlsoPlayingAnyMarkers
+			// 
+			this.selAlsoPlayingAnyMarkers.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.selAlsoPlayingAnyMarkers.Location = new System.Drawing.Point(10, 264);
+			this.selAlsoPlayingAnyMarkers.Name = "selAlsoPlayingAnyMarkers";
+			this.selAlsoPlayingAnyMarkers.SelectedItems = new string[0];
+			this.selAlsoPlayingAnyMarkers.Size = new System.Drawing.Size(263, 85);
+			this.selAlsoPlayingAnyMarkers.TabIndex = 1;
+			// 
 			// label4
 			// 
 			this.label4.AutoSize = true;
-			this.label4.Location = new System.Drawing.Point(10, 245);
+			this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label4.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label4.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label4.Location = new System.Drawing.Point(10, 247);
 			this.label4.Name = "label4";
 			this.label4.Size = new System.Drawing.Size(225, 13);
 			this.label4.TabIndex = 0;
 			this.label4.Text = "Require any of the following markers to be set:";
 			// 
+			// selAlsoPlayingNotMarkers
+			// 
+			this.selAlsoPlayingNotMarkers.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.selAlsoPlayingNotMarkers.Location = new System.Drawing.Point(10, 151);
+			this.selAlsoPlayingNotMarkers.Name = "selAlsoPlayingNotMarkers";
+			this.selAlsoPlayingNotMarkers.SelectedItems = new string[0];
+			this.selAlsoPlayingNotMarkers.Size = new System.Drawing.Size(263, 85);
+			this.selAlsoPlayingNotMarkers.TabIndex = 1;
+			// 
 			// label5
 			// 
 			this.label5.AutoSize = true;
-			this.label5.Location = new System.Drawing.Point(10, 132);
+			this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label5.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label5.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label5.Location = new System.Drawing.Point(10, 134);
 			this.label5.Name = "label5";
 			this.label5.Size = new System.Drawing.Size(232, 13);
 			this.label5.TabIndex = 0;
 			this.label5.Text = "Require none of the following markers to be set:";
 			// 
+			// selAlsoPlayingAllMarkers
+			// 
+			this.selAlsoPlayingAllMarkers.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.selAlsoPlayingAllMarkers.Location = new System.Drawing.Point(7, 39);
+			this.selAlsoPlayingAllMarkers.Name = "selAlsoPlayingAllMarkers";
+			this.selAlsoPlayingAllMarkers.SelectedItems = new string[0];
+			this.selAlsoPlayingAllMarkers.Size = new System.Drawing.Size(266, 85);
+			this.selAlsoPlayingAllMarkers.TabIndex = 1;
+			// 
 			// label6
 			// 
 			this.label6.AutoSize = true;
-			this.label6.Location = new System.Drawing.Point(7, 20);
+			this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label6.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label6.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label6.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label6.Location = new System.Drawing.Point(7, 22);
 			this.label6.Name = "label6";
 			this.label6.Size = new System.Drawing.Size(206, 13);
 			this.label6.TabIndex = 0;
 			this.label6.Text = "Require all the following markers to be set:";
 			// 
-			// grpInfo
-			// 
-			this.grpInfo.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left)));
-			this.grpInfo.Controls.Add(this.tableGeneral);
-			this.grpInfo.Location = new System.Drawing.Point(6, 6);
-			this.grpInfo.Name = "grpInfo";
-			this.grpInfo.Size = new System.Drawing.Size(362, 572);
-			this.grpInfo.TabIndex = 1;
-			this.grpInfo.TabStop = false;
-			this.grpInfo.Text = "Information";
-			// 
-			// tableGeneral
-			// 
-			this.tableGeneral.AllowDelete = false;
-			this.tableGeneral.AllowFavorites = false;
-			this.tableGeneral.AllowHelp = true;
-			this.tableGeneral.BackColor = System.Drawing.Color.Transparent;
-			this.tableGeneral.Data = null;
-			this.tableGeneral.Dock = System.Windows.Forms.DockStyle.Fill;
-			this.tableGeneral.HideAddField = true;
-			this.tableGeneral.HideSpeedButtons = true;
-			this.tableGeneral.Location = new System.Drawing.Point(3, 16);
-			this.tableGeneral.Name = "tableGeneral";
-			this.tableGeneral.PlaceholderText = null;
-			this.tableGeneral.RemoveCaption = "Remove";
-			this.tableGeneral.RowHeaderWidth = 130F;
-			this.tableGeneral.Size = new System.Drawing.Size(356, 553);
-			this.tableGeneral.Sorted = true;
-			this.tableGeneral.TabIndex = 0;
-			this.tableGeneral.UseAutoComplete = false;
-			// 
-			// pageScenes
+			// groupBox1
 			// 
-			this.pageScenes.Controls.Add(this.canvas);
-			this.pageScenes.Location = new System.Drawing.Point(4, 22);
-			this.pageScenes.Name = "pageScenes";
-			this.pageScenes.Padding = new System.Windows.Forms.Padding(3);
-			this.pageScenes.Size = new System.Drawing.Size(965, 584);
-			this.pageScenes.TabIndex = 1;
-			this.pageScenes.Text = "Scenes";
-			this.pageScenes.UseVisualStyleBackColor = true;
+			this.groupBox1.Controls.Add(this.selAnyMarkers);
+			this.groupBox1.Controls.Add(this.label3);
+			this.groupBox1.Controls.Add(this.selNotMarkers);
+			this.groupBox1.Controls.Add(this.label2);
+			this.groupBox1.Controls.Add(this.selAllMarkers);
+			this.groupBox1.Controls.Add(this.label7);
+			this.groupBox1.Location = new System.Drawing.Point(3, 3);
+			this.groupBox1.Name = "groupBox1";
+			this.groupBox1.Size = new System.Drawing.Size(283, 360);
+			this.groupBox1.TabIndex = 44;
+			this.groupBox1.TabStop = false;
+			this.groupBox1.Text = "Own markers";
 			// 
 			// selAnyMarkers
 			// 
 			this.selAnyMarkers.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.selAnyMarkers.Location = new System.Drawing.Point(10, 262);
+			this.selAnyMarkers.Location = new System.Drawing.Point(10, 264);
 			this.selAnyMarkers.Name = "selAnyMarkers";
 			this.selAnyMarkers.SelectedItems = new string[0];
 			this.selAnyMarkers.Size = new System.Drawing.Size(267, 85);
 			this.selAnyMarkers.TabIndex = 1;
 			// 
+			// label3
+			// 
+			this.label3.AutoSize = true;
+			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label3.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label3.Location = new System.Drawing.Point(10, 247);
+			this.label3.Name = "label3";
+			this.label3.Size = new System.Drawing.Size(225, 13);
+			this.label3.TabIndex = 0;
+			this.label3.Text = "Require any of the following markers to be set:";
+			// 
 			// selNotMarkers
 			// 
 			this.selNotMarkers.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.selNotMarkers.Location = new System.Drawing.Point(10, 149);
+			this.selNotMarkers.Location = new System.Drawing.Point(10, 151);
 			this.selNotMarkers.Name = "selNotMarkers";
 			this.selNotMarkers.SelectedItems = new string[0];
 			this.selNotMarkers.Size = new System.Drawing.Size(267, 85);
 			this.selNotMarkers.TabIndex = 1;
 			// 
+			// label2
+			// 
+			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label2.Location = new System.Drawing.Point(10, 134);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(232, 13);
+			this.label2.TabIndex = 0;
+			this.label2.Text = "Require none of the following markers to be set:";
+			// 
 			// selAllMarkers
 			// 
 			this.selAllMarkers.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.selAllMarkers.Location = new System.Drawing.Point(7, 37);
+			this.selAllMarkers.Location = new System.Drawing.Point(7, 39);
 			this.selAllMarkers.Name = "selAllMarkers";
 			this.selAllMarkers.SelectedItems = new string[0];
 			this.selAllMarkers.Size = new System.Drawing.Size(270, 85);
 			this.selAllMarkers.TabIndex = 1;
 			// 
-			// selAlsoPlayingAnyMarkers
+			// label7
 			// 
-			this.selAlsoPlayingAnyMarkers.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.selAlsoPlayingAnyMarkers.Location = new System.Drawing.Point(10, 262);
-			this.selAlsoPlayingAnyMarkers.Name = "selAlsoPlayingAnyMarkers";
-			this.selAlsoPlayingAnyMarkers.SelectedItems = new string[0];
-			this.selAlsoPlayingAnyMarkers.Size = new System.Drawing.Size(263, 85);
-			this.selAlsoPlayingAnyMarkers.TabIndex = 1;
+			this.label7.AutoSize = true;
+			this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label7.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label7.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label7.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label7.Location = new System.Drawing.Point(7, 22);
+			this.label7.Name = "label7";
+			this.label7.Size = new System.Drawing.Size(206, 13);
+			this.label7.TabIndex = 0;
+			this.label7.Text = "Require all the following markers to be set:";
 			// 
-			// selAlsoPlayingNotMarkers
+			// grpInfo
 			// 
-			this.selAlsoPlayingNotMarkers.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.selAlsoPlayingNotMarkers.Location = new System.Drawing.Point(10, 149);
-			this.selAlsoPlayingNotMarkers.Name = "selAlsoPlayingNotMarkers";
-			this.selAlsoPlayingNotMarkers.SelectedItems = new string[0];
-			this.selAlsoPlayingNotMarkers.Size = new System.Drawing.Size(263, 85);
-			this.selAlsoPlayingNotMarkers.TabIndex = 1;
+			this.grpInfo.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+			this.grpInfo.Controls.Add(this.tableGeneral);
+			this.grpInfo.Location = new System.Drawing.Point(6, 6);
+			this.grpInfo.Name = "grpInfo";
+			this.grpInfo.Size = new System.Drawing.Size(362, 551);
+			this.grpInfo.TabIndex = 1;
+			this.grpInfo.TabStop = false;
+			this.grpInfo.Text = "Information";
 			// 
-			// selAlsoPlayingAllMarkers
+			// tableGeneral
 			// 
-			this.selAlsoPlayingAllMarkers.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+			this.tableGeneral.AllowDelete = false;
+			this.tableGeneral.AllowFavorites = false;
+			this.tableGeneral.AllowHelp = true;
+			this.tableGeneral.AllowMacros = false;
+			this.tableGeneral.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.selAlsoPlayingAllMarkers.Location = new System.Drawing.Point(7, 37);
-			this.selAlsoPlayingAllMarkers.Name = "selAlsoPlayingAllMarkers";
-			this.selAlsoPlayingAllMarkers.SelectedItems = new string[0];
-			this.selAlsoPlayingAllMarkers.Size = new System.Drawing.Size(266, 85);
-			this.selAlsoPlayingAllMarkers.TabIndex = 1;
+			this.tableGeneral.BackColor = System.Drawing.Color.White;
+			this.tableGeneral.Data = null;
+			this.tableGeneral.HeaderType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.tableGeneral.HideAddField = true;
+			this.tableGeneral.HideSpeedButtons = true;
+			this.tableGeneral.Location = new System.Drawing.Point(3, 22);
+			this.tableGeneral.ModifyingProperty = null;
+			this.tableGeneral.Name = "tableGeneral";
+			this.tableGeneral.PanelType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.tableGeneral.PlaceholderText = null;
+			this.tableGeneral.PreserveControls = false;
+			this.tableGeneral.PreviewData = null;
+			this.tableGeneral.RemoveCaption = "Remove";
+			this.tableGeneral.RowHeaderWidth = 130F;
+			this.tableGeneral.RunInitialAddEvents = false;
+			this.tableGeneral.Size = new System.Drawing.Size(356, 526);
+			this.tableGeneral.Sorted = true;
+			this.tableGeneral.TabIndex = 0;
+			this.tableGeneral.UndoManager = null;
+			this.tableGeneral.UseAutoComplete = false;
+			// 
+			// pageScenes
+			// 
+			this.pageScenes.BackColor = System.Drawing.Color.White;
+			this.pageScenes.Controls.Add(this.canvas);
+			this.pageScenes.Location = new System.Drawing.Point(4, 22);
+			this.pageScenes.Name = "pageScenes";
+			this.pageScenes.Size = new System.Drawing.Size(965, 563);
+			this.pageScenes.TabIndex = 1;
+			this.pageScenes.Text = "Scenes";
 			// 
 			// canvas
 			// 
 			this.canvas.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.canvas.Enabled = false;
-			this.canvas.Location = new System.Drawing.Point(3, 3);
+			this.canvas.Location = new System.Drawing.Point(0, 0);
+			this.canvas.Margin = new System.Windows.Forms.Padding(0);
 			this.canvas.Name = "canvas";
-			this.canvas.Size = new System.Drawing.Size(959, 578);
+			this.canvas.Size = new System.Drawing.Size(965, 563);
 			this.canvas.TabIndex = 23;
 			this.canvas.ZoomLevel = 1F;
 			// 
+			// strip
+			// 
+			this.strip.Dock = System.Windows.Forms.DockStyle.Top;
+			this.strip.Location = new System.Drawing.Point(0, 36);
+			this.strip.Margin = new System.Windows.Forms.Padding(0);
+			this.strip.Name = "strip";
+			this.strip.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
+			this.strip.ShowCloseButton = false;
+			this.strip.Size = new System.Drawing.Size(973, 23);
+			this.strip.StartMargin = 5;
+			this.strip.TabControl = this.tabs;
+			this.strip.TabIndex = 22;
+			this.strip.TabMargin = 5;
+			this.strip.TabPadding = 20;
+			this.strip.TabSize = -1;
+			this.strip.TabType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.strip.Text = "skinnedTabStrip1";
+			this.strip.Vertical = false;
+			// 
 			// EpilogueEditor
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.strip);
 			this.Controls.Add(this.tabs);
 			this.Controls.Add(this.pnlHeader);
 			this.Name = "EpilogueEditor";
@@ -395,10 +476,10 @@
 			this.pageGeneral.ResumeLayout(false);
 			this.grpConditions.ResumeLayout(false);
 			this.tableLayoutPanel1.ResumeLayout(false);
-			this.groupBox1.ResumeLayout(false);
-			this.groupBox1.PerformLayout();
 			this.groupBox2.ResumeLayout(false);
 			this.groupBox2.PerformLayout();
+			this.groupBox1.ResumeLayout(false);
+			this.groupBox1.PerformLayout();
 			this.grpInfo.ResumeLayout(false);
 			this.pageScenes.ResumeLayout(false);
 			this.ResumeLayout(false);
@@ -406,35 +487,36 @@
 		}
 
 		#endregion
-		private System.Windows.Forms.ComboBox cboEnding;
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedComboBox cboEnding;
+		private Desktop.Skinning.SkinnedLabel label1;
 		private System.Windows.Forms.OpenFileDialog imageFileDialog;
-		private System.Windows.Forms.Button cmdDeleteEnding;
-		private System.Windows.Forms.Button cmdAddEnding;
+		private Desktop.Skinning.SkinnedButton cmdDeleteEnding;
+		private Desktop.Skinning.SkinnedButton cmdAddEnding;
 		private System.Windows.Forms.ToolTip toolTip1;
 		private EpilogueCanvas canvas;
-		private System.Windows.Forms.Panel pnlHeader;
+		private Desktop.Skinning.SkinnedPanel pnlHeader;
 		private System.Windows.Forms.Timer tmrActivate;
-		private System.Windows.Forms.TabControl tabs;
+		private Desktop.Skinning.SkinnedTabControl tabs;
 		private System.Windows.Forms.TabPage pageGeneral;
 		private System.Windows.Forms.TabPage pageScenes;
 		private Desktop.CommonControls.PropertyTable tableGeneral;
-		private System.Windows.Forms.GroupBox grpInfo;
-		private System.Windows.Forms.GroupBox grpConditions;
-		private System.Windows.Forms.GroupBox groupBox2;
+		private Desktop.Skinning.SkinnedGroupBox grpInfo;
+		private Desktop.Skinning.SkinnedGroupBox grpConditions;
+		private Desktop.Skinning.SkinnedGroupBox groupBox2;
 		private SelectBox selAlsoPlayingAnyMarkers;
-		private System.Windows.Forms.Label label4;
+		private Desktop.Skinning.SkinnedLabel label4;
 		private SelectBox selAlsoPlayingNotMarkers;
-		private System.Windows.Forms.Label label5;
+		private Desktop.Skinning.SkinnedLabel label5;
 		private SelectBox selAlsoPlayingAllMarkers;
-		private System.Windows.Forms.Label label6;
-		private System.Windows.Forms.GroupBox groupBox1;
+		private Desktop.Skinning.SkinnedLabel label6;
+		private Desktop.Skinning.SkinnedGroupBox groupBox1;
 		private SelectBox selAnyMarkers;
-		private System.Windows.Forms.Label label3;
+		private Desktop.Skinning.SkinnedLabel label3;
 		private SelectBox selNotMarkers;
-		private System.Windows.Forms.Label label2;
+		private Desktop.Skinning.SkinnedLabel label2;
 		private SelectBox selAllMarkers;
-		private System.Windows.Forms.Label label7;
+		private Desktop.Skinning.SkinnedLabel label7;
 		private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
+		private Desktop.Skinning.SkinnedTabStrip strip;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/EpilogueEditor.cs b/editor source/SPNATI Character Editor/Controls/EpilogueEditor.cs
index 76a57b614dbd520073a40d25264835d197a80ff8..d5867038eaed5f5d469200e6edd810567c3ae0c1 100644
--- a/editor source/SPNATI Character Editor/Controls/EpilogueEditor.cs	
+++ b/editor source/SPNATI Character Editor/Controls/EpilogueEditor.cs	
@@ -165,7 +165,7 @@ namespace SPNATI_Character_Editor.Controls
 		private void LoadEnding(Epilogue ending)
 		{
 			SaveEnding();
-			Config.LastEnding = ending.Title;
+			Config.LastEnding = ending?.Title ?? "";
 			_ending = ending;
 			PopulateDataFields();
 			cmdDeleteEnding.Enabled = tabs.Enabled = (ending != null);
diff --git a/editor source/SPNATI Character Editor/Controls/FindReplace.Designer.cs b/editor source/SPNATI Character Editor/Controls/FindReplace.Designer.cs
index 3f15e14e2b925b11c5f4cbeb4fa697dd9e31de95..dfad5df9960b2d503e9f1f64287eedc4c6874f90 100644
--- a/editor source/SPNATI Character Editor/Controls/FindReplace.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/FindReplace.Designer.cs	
@@ -29,59 +29,64 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			this.tabs = new System.Windows.Forms.TabControl();
+			this.tabs = new Desktop.Skinning.SkinnedTabControl();
 			this.tabFind = new System.Windows.Forms.TabPage();
 			this.tabReplace = new System.Windows.Forms.TabPage();
-			this.lblFind = new System.Windows.Forms.Label();
-			this.txtFind = new System.Windows.Forms.TextBox();
-			this.chkMatchCase = new System.Windows.Forms.CheckBox();
-			this.chkWholeWords = new System.Windows.Forms.CheckBox();
-			this.cmdFind = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.txtReplace = new System.Windows.Forms.TextBox();
-			this.lblReplace = new System.Windows.Forms.Label();
-			this.cmdReplace = new System.Windows.Forms.Button();
-			this.cmdReplaceAll = new System.Windows.Forms.Button();
+			this.lblFind = new Desktop.Skinning.SkinnedLabel();
+			this.txtFind = new Desktop.Skinning.SkinnedTextBox();
+			this.chkMatchCase = new Desktop.Skinning.SkinnedCheckBox();
+			this.chkWholeWords = new Desktop.Skinning.SkinnedCheckBox();
+			this.cmdFind = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.txtReplace = new Desktop.Skinning.SkinnedTextBox();
+			this.lblReplace = new Desktop.Skinning.SkinnedLabel();
+			this.cmdReplace = new Desktop.Skinning.SkinnedButton();
+			this.cmdReplaceAll = new Desktop.Skinning.SkinnedButton();
 			this.focusTimer = new System.Windows.Forms.Timer(this.components);
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.strip = new Desktop.Skinning.SkinnedTabStrip();
 			this.tabs.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// tabs
 			// 
+			this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
 			this.tabs.Controls.Add(this.tabFind);
 			this.tabs.Controls.Add(this.tabReplace);
-			this.tabs.Dock = System.Windows.Forms.DockStyle.Top;
-			this.tabs.Location = new System.Drawing.Point(0, 0);
+			this.tabs.Location = new System.Drawing.Point(2, 27);
 			this.tabs.Name = "tabs";
 			this.tabs.SelectedIndex = 0;
-			this.tabs.Size = new System.Drawing.Size(399, 26);
+			this.tabs.Size = new System.Drawing.Size(390, 26);
 			this.tabs.TabIndex = 20;
 			this.tabs.SelectedIndexChanged += new System.EventHandler(this.tabs_SelectedIndexChanged);
 			// 
 			// tabFind
 			// 
+			this.tabFind.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(225)))), ((int)(((byte)(225)))), ((int)(((byte)(230)))));
 			this.tabFind.Location = new System.Drawing.Point(4, 22);
 			this.tabFind.Name = "tabFind";
 			this.tabFind.Padding = new System.Windows.Forms.Padding(3);
-			this.tabFind.Size = new System.Drawing.Size(391, 0);
+			this.tabFind.Size = new System.Drawing.Size(382, 0);
 			this.tabFind.TabIndex = 0;
 			this.tabFind.Text = "Find";
-			this.tabFind.UseVisualStyleBackColor = true;
 			// 
 			// tabReplace
 			// 
+			this.tabReplace.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(225)))), ((int)(((byte)(225)))), ((int)(((byte)(230)))));
 			this.tabReplace.Location = new System.Drawing.Point(4, 22);
 			this.tabReplace.Name = "tabReplace";
 			this.tabReplace.Padding = new System.Windows.Forms.Padding(3);
 			this.tabReplace.Size = new System.Drawing.Size(391, 0);
 			this.tabReplace.TabIndex = 1;
 			this.tabReplace.Text = "Replace";
-			this.tabReplace.UseVisualStyleBackColor = true;
 			// 
 			// lblFind
 			// 
 			this.lblFind.AutoSize = true;
-			this.lblFind.Location = new System.Drawing.Point(12, 31);
+			this.lblFind.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblFind.Location = new System.Drawing.Point(12, 58);
 			this.lblFind.Name = "lblFind";
 			this.lblFind.Size = new System.Drawing.Size(56, 13);
 			this.lblFind.TabIndex = 0;
@@ -89,15 +94,18 @@
 			// 
 			// txtFind
 			// 
-			this.txtFind.Location = new System.Drawing.Point(90, 28);
+			this.txtFind.BackColor = System.Drawing.Color.White;
+			this.txtFind.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtFind.ForeColor = System.Drawing.Color.Black;
+			this.txtFind.Location = new System.Drawing.Point(90, 55);
 			this.txtFind.Name = "txtFind";
-			this.txtFind.Size = new System.Drawing.Size(203, 20);
+			this.txtFind.Size = new System.Drawing.Size(196, 20);
 			this.txtFind.TabIndex = 1;
 			// 
 			// chkMatchCase
 			// 
 			this.chkMatchCase.AutoSize = true;
-			this.chkMatchCase.Location = new System.Drawing.Point(15, 80);
+			this.chkMatchCase.Location = new System.Drawing.Point(15, 107);
 			this.chkMatchCase.Name = "chkMatchCase";
 			this.chkMatchCase.Size = new System.Drawing.Size(82, 17);
 			this.chkMatchCase.TabIndex = 4;
@@ -107,7 +115,7 @@
 			// chkWholeWords
 			// 
 			this.chkWholeWords.AutoSize = true;
-			this.chkWholeWords.Location = new System.Drawing.Point(15, 103);
+			this.chkWholeWords.Location = new System.Drawing.Point(15, 130);
 			this.chkWholeWords.Name = "chkWholeWords";
 			this.chkWholeWords.Size = new System.Drawing.Size(130, 17);
 			this.chkWholeWords.TabIndex = 5;
@@ -117,9 +125,10 @@
 			// cmdFind
 			// 
 			this.cmdFind.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdFind.Location = new System.Drawing.Point(312, 26);
+			this.cmdFind.Flat = false;
+			this.cmdFind.Location = new System.Drawing.Point(292, 53);
 			this.cmdFind.Name = "cmdFind";
-			this.cmdFind.Size = new System.Drawing.Size(75, 23);
+			this.cmdFind.Size = new System.Drawing.Size(95, 23);
 			this.cmdFind.TabIndex = 50;
 			this.cmdFind.Text = "&Find Next";
 			this.cmdFind.UseVisualStyleBackColor = true;
@@ -128,9 +137,10 @@
 			// 
 			// cmdCancel
 			// 
-			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(312, 113);
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.Location = new System.Drawing.Point(320, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 53;
@@ -140,15 +150,19 @@
 			// 
 			// txtReplace
 			// 
-			this.txtReplace.Location = new System.Drawing.Point(90, 54);
+			this.txtReplace.BackColor = System.Drawing.Color.White;
+			this.txtReplace.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtReplace.ForeColor = System.Drawing.Color.Black;
+			this.txtReplace.Location = new System.Drawing.Point(90, 81);
 			this.txtReplace.Name = "txtReplace";
-			this.txtReplace.Size = new System.Drawing.Size(203, 20);
+			this.txtReplace.Size = new System.Drawing.Size(196, 20);
 			this.txtReplace.TabIndex = 3;
 			// 
 			// lblReplace
 			// 
 			this.lblReplace.AutoSize = true;
-			this.lblReplace.Location = new System.Drawing.Point(12, 57);
+			this.lblReplace.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblReplace.Location = new System.Drawing.Point(12, 84);
 			this.lblReplace.Name = "lblReplace";
 			this.lblReplace.Size = new System.Drawing.Size(72, 13);
 			this.lblReplace.TabIndex = 2;
@@ -157,9 +171,10 @@
 			// cmdReplace
 			// 
 			this.cmdReplace.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdReplace.Location = new System.Drawing.Point(312, 55);
+			this.cmdReplace.Flat = false;
+			this.cmdReplace.Location = new System.Drawing.Point(292, 81);
 			this.cmdReplace.Name = "cmdReplace";
-			this.cmdReplace.Size = new System.Drawing.Size(75, 23);
+			this.cmdReplace.Size = new System.Drawing.Size(95, 23);
 			this.cmdReplace.TabIndex = 51;
 			this.cmdReplace.Text = "&Replace";
 			this.cmdReplace.UseVisualStyleBackColor = true;
@@ -169,9 +184,10 @@
 			// cmdReplaceAll
 			// 
 			this.cmdReplaceAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdReplaceAll.Location = new System.Drawing.Point(312, 84);
+			this.cmdReplaceAll.Flat = false;
+			this.cmdReplaceAll.Location = new System.Drawing.Point(292, 110);
 			this.cmdReplaceAll.Name = "cmdReplaceAll";
-			this.cmdReplaceAll.Size = new System.Drawing.Size(75, 23);
+			this.cmdReplaceAll.Size = new System.Drawing.Size(95, 23);
 			this.cmdReplaceAll.TabIndex = 52;
 			this.cmdReplaceAll.Text = "Replace &All";
 			this.cmdReplaceAll.UseVisualStyleBackColor = true;
@@ -182,33 +198,63 @@
 			// 
 			this.focusTimer.Tick += new System.EventHandler(this.focusTimer_Tick);
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 153);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(399, 30);
+			this.skinnedPanel1.TabIndex = 54;
+			// 
+			// strip
+			// 
+			this.strip.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.strip.Location = new System.Drawing.Point(1, 25);
+			this.strip.Margin = new System.Windows.Forms.Padding(0);
+			this.strip.Name = "strip";
+			this.strip.Size = new System.Drawing.Size(397, 23);
+			this.strip.TabControl = this.tabs;
+			this.strip.TabIndex = 55;
+			this.strip.TabPadding = 20;
+			this.strip.TabSize = -1;
+			this.strip.Text = "skinnedTabStrip1";
+			this.strip.Vertical = false;
+			// 
 			// FindReplace
 			// 
 			this.AcceptButton = this.cmdFind;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(399, 148);
+			this.ClientSize = new System.Drawing.Size(399, 183);
+			this.Controls.Add(this.strip);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.cmdReplaceAll);
 			this.Controls.Add(this.cmdReplace);
 			this.Controls.Add(this.lblReplace);
 			this.Controls.Add(this.txtReplace);
-			this.Controls.Add(this.cmdCancel);
 			this.Controls.Add(this.tabs);
 			this.Controls.Add(this.cmdFind);
 			this.Controls.Add(this.txtFind);
 			this.Controls.Add(this.chkWholeWords);
 			this.Controls.Add(this.lblFind);
 			this.Controls.Add(this.chkMatchCase);
+			this.MaximizeBox = false;
+			this.MinimizeBox = false;
 			this.Name = "FindReplace";
 			this.ShowIcon = false;
 			this.ShowInTaskbar = false;
+			this.Sizable = false;
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Find and Replace";
 			this.TopMost = true;
 			this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FindReplace_FormClosing);
 			this.Shown += new System.EventHandler(this.FindReplace_Shown);
 			this.tabs.ResumeLayout(false);
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -216,19 +262,21 @@
 
 		#endregion
 
-		private System.Windows.Forms.TabControl tabs;
+		private Desktop.Skinning.SkinnedTabControl tabs;
 		private System.Windows.Forms.TabPage tabFind;
 		private System.Windows.Forms.TabPage tabReplace;
-		private System.Windows.Forms.TextBox txtFind;
-		private System.Windows.Forms.Label lblFind;
-		private System.Windows.Forms.CheckBox chkWholeWords;
-		private System.Windows.Forms.CheckBox chkMatchCase;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.Button cmdFind;
-		private System.Windows.Forms.TextBox txtReplace;
-		private System.Windows.Forms.Label lblReplace;
-		private System.Windows.Forms.Button cmdReplace;
-		private System.Windows.Forms.Button cmdReplaceAll;
+		private Desktop.Skinning.SkinnedTextBox txtFind;
+		private Desktop.Skinning.SkinnedLabel lblFind;
+		private Desktop.Skinning.SkinnedCheckBox chkWholeWords;
+		private Desktop.Skinning.SkinnedCheckBox chkMatchCase;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedButton cmdFind;
+		private Desktop.Skinning.SkinnedTextBox txtReplace;
+		private Desktop.Skinning.SkinnedLabel lblReplace;
+		private Desktop.Skinning.SkinnedButton cmdReplace;
+		private Desktop.Skinning.SkinnedButton cmdReplaceAll;
 		private System.Windows.Forms.Timer focusTimer;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.Skinning.SkinnedTabStrip strip;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/FindReplace.cs b/editor source/SPNATI Character Editor/Controls/FindReplace.cs
index b0f66b2ca64d5914d41c7b4dd122b177ad5dae32..04f4b1b647e47d3d91e78446dc1b9f66ba4d60d6 100644
--- a/editor source/SPNATI Character Editor/Controls/FindReplace.cs	
+++ b/editor source/SPNATI Character Editor/Controls/FindReplace.cs	
@@ -1,9 +1,10 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor
 {
-	public partial class FindReplace : Form
+	public partial class FindReplace : SkinnedForm
 	{
 		public event EventHandler<FindArgs> Find;
 		public event EventHandler<FindArgs> Replace;
diff --git a/editor source/SPNATI Character Editor/Controls/IntellisenseControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/IntellisenseControl.Designer.cs
index 2b207afeee9a61441e4472fb5fe6bc407292a043..55002f6946640843f78f2aead52bfdc4366a90cd 100644
--- a/editor source/SPNATI Character Editor/Controls/IntellisenseControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/IntellisenseControl.Designer.cs	
@@ -28,8 +28,8 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.lstItems = new System.Windows.Forms.ListBox();
-			this.lblTooltip = new System.Windows.Forms.LinkLabel();
+			this.lstItems = new Desktop.Skinning.SkinnedListBox();
+			this.lblTooltip = new Desktop.Skinning.SkinnedLinkLabel();
 			this.SuspendLayout();
 			// 
 			// lstItems
@@ -75,7 +75,7 @@
 
 		#endregion
 
-		private System.Windows.Forms.ListBox lstItems;
-		private System.Windows.Forms.LinkLabel lblTooltip;
+		private Desktop.Skinning.SkinnedListBox lstItems;
+		private Desktop.Skinning.SkinnedLinkLabel lblTooltip;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/IntellisenseControl.cs b/editor source/SPNATI Character Editor/Controls/IntellisenseControl.cs
index 15d882969db7f8749c4a7cd05333a04a2e6c2530..ad91dc30b40366df8b62d5ffc4797262a8331293 100644
--- a/editor source/SPNATI Character Editor/Controls/IntellisenseControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/IntellisenseControl.cs	
@@ -1,4 +1,5 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Collections.Generic;
 using System.Drawing;
 using System.Linq;
@@ -7,7 +8,7 @@ using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Controls
 {
-	public partial class IntellisenseControl : UserControl
+	public partial class IntellisenseControl : UserControl, ISkinControl
 	{
 		[DllImport("user32")]
 		private extern static int GetCaretPos(out Point p);
@@ -20,6 +21,7 @@ namespace SPNATI_Character_Editor.Controls
 		private IntellisenseContext _lastContext = new IntellisenseContext(ContextType.None, -1);
 
 		private List<string> _availableVars = new List<string>();
+		private List<string> _availableStyles = new List<string>();
 
 		public event EventHandler<InsertEventArgs> InsertSnippet;
 
@@ -56,8 +58,8 @@ namespace SPNATI_Character_Editor.Controls
 		{
 			switch (code)
 			{
-				case Keys.Shift:
-				case Keys.ShiftKey:
+				//case Keys.Shift:
+				//case Keys.ShiftKey:
 				case Keys.Control:
 				case Keys.ControlKey:
 				case Keys.Alt:
@@ -85,7 +87,7 @@ namespace SPNATI_Character_Editor.Controls
 
 			if (_lastContext.Context != ContextType.None && keyCode != Keys.Back && keyCode != Keys.Delete && keyCode != Keys.None)
 			{
-				if (_lastContext.Context != ContextType.VariableName || _lastContext.VariableName != "")
+				if ((_lastContext.Context != ContextType.VariableName && _lastContext.Context != ContextType.StyleName) || _lastContext.VariableName != "")
 				{
 					char lastChar = _textBox.Text[_textBox.SelectionStart - 1];
 					switch (_lastContext.Context)
@@ -101,6 +103,14 @@ namespace SPNATI_Character_Editor.Controls
 								}
 							}
 							break;
+						case ContextType.StyleName:
+							if (lastChar == ',' || lastChar == '?' || lastChar == ';' || 
+								lastChar == ' ' || lastChar == '}' || lastChar == '.' || lastChar == ',')
+							{
+								AutoComplete(lastChar);
+								return;
+							}
+							break;
 						case ContextType.FunctionName:
 							if (lastChar == '(' || lastChar == '~')
 							{
@@ -128,6 +138,9 @@ namespace SPNATI_Character_Editor.Controls
 				case ContextType.VariableName:
 					UpdateVariableList(_lastContext.VariableName);
 					break;
+				case ContextType.StyleName:
+					UpdateStyleList(_lastContext.VariableName);
+					break;
 				case ContextType.FunctionName:
 					UpdateFunctionList(_lastContext.FunctionName);
 					DisplayTooltip();
@@ -184,6 +197,28 @@ namespace SPNATI_Character_Editor.Controls
 						InsertSnippet?.Invoke(this, new InsertEventArgs(insertionText, insertion, selectionLength));
 						UpdateIntellisense(Keys.None);
 						break;
+					case ContextType.StyleName:
+						StyleRule rule = StyleDatabase.Get(value, _character);
+						text = _lastContext.VariableName;
+						insertion += text.Length + 1;
+						insertionText = rule.ClassName.Substring(text.Length, rule.ClassName.Length - text.Length);
+						if (endingChar != '}')
+						{
+							insertionText += "}";
+						}
+						if (endingChar != '\0')
+						{
+							insertionText += endingChar;
+							selectionLength++;
+						}
+						else
+						{
+							endingChar = '}';
+						}
+						_lastContext.Context = ContextType.None;
+						InsertSnippet?.Invoke(this, new InsertEventArgs(insertionText, insertion, selectionLength));
+						UpdateIntellisense(Keys.None);
+						break;
 					case ContextType.FunctionName:
 						text = _lastContext.FunctionName;
 						string varName = _lastContext.VariableName;
@@ -242,7 +277,32 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				_availableVars.Add(v.Name);
 			}
+			foreach (TargetCondition condition in stageCase.Conditions)
+			{
+				if (!string.IsNullOrEmpty(condition.Variable))
+				{
+					_availableVars.Add(condition.Variable);
+				}
+			}
 			_availableVars.Sort();
+
+			_availableStyles.Clear();
+			foreach (StyleRule s in StyleDatabase.GlobalStyles)
+			{
+				_availableStyles.Add(s.ClassName);
+			}
+			if (_character != null)
+			{
+				CharacterStyleSheet sheet = _character.Styles;
+				if (sheet != null)
+				{
+					foreach (StyleRule rule in sheet.Rules)
+					{
+						_availableStyles.Add(rule.ClassName);
+					}
+				}
+			}
+			_availableStyles.Sort();
 		}
 
 		private void Display()
@@ -282,6 +342,24 @@ namespace SPNATI_Character_Editor.Controls
 			}
 		}
 
+		private void UpdateStyleList(string style)
+		{
+			lstItems.DataSource = null;
+			List<string> options = _availableStyles
+				.Where(var => string.IsNullOrEmpty(style) || var.ToLower().StartsWith(style.ToLower()))
+				.Select(var => var).ToList();
+
+			if (options.Count > 0)
+			{
+				lstItems.DataSource = options;
+				Display();
+			}
+			else
+			{
+				Hide();
+			}
+		}
+
 		private void UpdateFunctionList(string function)
 		{
 			if (_lastContext.Context != ContextType.FunctionName && _lastContext.Context != ContextType.Parameter) { return; }
@@ -326,6 +404,14 @@ namespace SPNATI_Character_Editor.Controls
 						lblTooltip.Links.Clear();
 					}
 					break;
+				case ContextType.StyleName:
+					StyleRule style = StyleDatabase.Get(value, _character);
+					if (style != null)
+					{
+						lblTooltip.Text = style.Description;
+						lblTooltip.Links.Clear();
+					}
+					break;
 				case ContextType.FunctionName:
 					v = VariableDatabase.Get(_lastContext.VariableName);
 					if (v != null)
@@ -383,6 +469,11 @@ namespace SPNATI_Character_Editor.Controls
 		{
 			DisplayTooltip();
 		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			BackColor = skin.Surface.Normal;
+		}
 	}
 
 	public struct InsertEventArgs
diff --git a/editor source/SPNATI Character Editor/Controls/IntervalSlider.Designer.cs b/editor source/SPNATI Character Editor/Controls/IntervalSlider.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ac31b72c148a1b628f6a2f8fbbcabed5f2dd1ff9
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/IntervalSlider.Designer.cs	
@@ -0,0 +1,79 @@
+namespace SPNATI_Character_Editor.Controls
+{
+	partial class IntervalSlider
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.components = new System.ComponentModel.Container();
+			this.mnuOptions = new System.Windows.Forms.ContextMenuStrip(this.components);
+			this.tsAdd = new System.Windows.Forms.ToolStripMenuItem();
+			this.tsRemove = new System.Windows.Forms.ToolStripMenuItem();
+			this.mnuOptions.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// mnuOptions
+			// 
+			this.mnuOptions.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.tsAdd,
+            this.tsRemove});
+			this.mnuOptions.Name = "mnuOptions";
+			this.mnuOptions.Size = new System.Drawing.Size(144, 48);
+			this.mnuOptions.Opening += new System.ComponentModel.CancelEventHandler(this.mnuOptions_Opening);
+			// 
+			// tsAdd
+			// 
+			this.tsAdd.Image = global::SPNATI_Character_Editor.Properties.Resources.Add;
+			this.tsAdd.Name = "tsAdd";
+			this.tsAdd.Size = new System.Drawing.Size(143, 22);
+			this.tsAdd.Text = "Add Split";
+			// 
+			// tsRemove
+			// 
+			this.tsRemove.Image = global::SPNATI_Character_Editor.Properties.Resources.Remove;
+			this.tsRemove.Name = "tsRemove";
+			this.tsRemove.Size = new System.Drawing.Size(143, 22);
+			this.tsRemove.Text = "Remove Split";
+			// 
+			// IntervalSlider
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.ContextMenuStrip = this.mnuOptions;
+			this.Name = "IntervalSlider";
+			this.Size = new System.Drawing.Size(150, 21);
+			this.mnuOptions.ResumeLayout(false);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private System.Windows.Forms.ContextMenuStrip mnuOptions;
+		private System.Windows.Forms.ToolStripMenuItem tsAdd;
+		private System.Windows.Forms.ToolStripMenuItem tsRemove;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/IntervalSlider.cs b/editor source/SPNATI Character Editor/Controls/IntervalSlider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2cd5ab6fcead889c0ae3988d86f1a853087d4cc8
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/IntervalSlider.cs	
@@ -0,0 +1,357 @@
+using Desktop.Skinning;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Controls
+{
+	public partial class IntervalSlider : UserControl, ISkinControl, ISupportInitialize
+	{
+		public ObservableCollection<int> Splits = new ObservableCollection<int>();
+		private Dictionary<int, Rectangle> _splitRects = new Dictionary<int, Rectangle>();
+
+		private Font _font;
+		private bool _dragging;
+		private int _selectedSplit = -1;
+		private int _hoverSplit = -1;
+
+		public IntervalSlider()
+		{
+			InitializeComponent();
+			DoubleBuffered = true;
+			SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+			SetStyle(ControlStyles.UserMouse, true);
+			SetStyle(ControlStyles.UserPaint, true);
+			SetStyle(ControlStyles.ContainerControl, true);
+			SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
+			SetStyle(ControlStyles.Selectable, true);
+			SetStyle(ControlStyles.ResizeRedraw, true);
+			SetStyle(ControlStyles.SupportsTransparentBackColor, true);
+			Splits.CollectionChanged += Splits_CollectionChanged;
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+
+			tsAdd.Click += TsAdd_Click;
+			tsRemove.Click += TsRemove_Click;
+		}
+
+		private void Splits_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+		{
+			UpdateRects();
+			Invalidate();
+		}
+
+		private void UpdateRects()
+		{
+			_splitRects.Clear();
+			Rectangle trackRect = GetTrackRectangle();
+			foreach (int split in Splits)
+			{
+				float pct = (split - 0.5f - Minimum) / (Maximum - Minimum);
+				int left = (int)(trackRect.Left + pct * trackRect.Width);
+				Rectangle thumbRect = new Rectangle(left - MarkerSize / 2, trackRect.Y + trackRect.Height / 2 - MarkerSize / 2, MarkerSize, MarkerSize);
+				_splitRects[split] = thumbRect;
+			}
+		}
+
+		private int _minimum = 0;
+		public int Minimum
+		{
+			get { return _minimum; }
+			set { _minimum = value; }
+		}
+
+		private int _maximum = 10;
+		public int Maximum
+		{
+			get { return _maximum; }
+			set { _maximum = value; }
+		}
+
+		private SkinnedFieldType _fieldType = SkinnedFieldType.Primary;
+		public SkinnedFieldType FieldType
+		{
+			get { return _fieldType; }
+			set
+			{
+				_fieldType = value;
+				_colorSet = null;
+				Invalidate(true);
+			}
+		}
+
+		private ColorSet _colorSet;
+		private ColorSet _lightColorSet;
+
+		public VisualState MouseState { get; private set; }
+
+		private const int MarkerSize = 10;
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			_colorSet = null;
+			_font = new Font(Skin.TextFont.FontFamily, 6, FontStyle.Regular);
+		}
+
+		public void BeginInit()
+		{
+		}
+
+		public void EndInit()
+		{
+		}
+
+		/// <summary>
+		/// Moves the slider thumb to the given point
+		/// </summary>
+		/// <param name="x"></param>
+		private void MoveSlider(int x)
+		{
+			if (_selectedSplit == -1) { return; }
+			Rectangle track = GetTrackRectangle();
+			float amount = (x - track.X) / (float)track.Width;
+			int target = (int)(Minimum + amount * (Maximum - Minimum));
+			target += 1;
+			if (target <= Minimum || target > Maximum) { return; }
+			int current = Splits[_selectedSplit];
+			for (int i = 0; i < Splits.Count; i++)
+			{
+				if (i == _selectedSplit) { continue; }
+				if (current <= Splits[i] && Splits[i] <= target ||
+					target <= Splits[i] && Splits[i] <= current)
+				{
+					return;
+				}
+			}
+			if (target != current)
+			{
+				Splits[_selectedSplit] = target;
+				UpdateRects();
+				Invalidate();
+			}
+		}
+
+		protected override void OnMouseMove(MouseEventArgs e)
+		{
+			if (_dragging)
+			{
+				MoveSlider(e.X);
+			}
+			int oldSplit = _hoverSplit;
+			_hoverSplit = -1;
+			foreach (KeyValuePair<int, Rectangle> kvp in _splitRects)
+			{
+				Rectangle thumbRect = kvp.Value;
+				if (thumbRect.Contains(new Point(e.X, e.Y)))
+				{
+					_hoverSplit = Splits.IndexOf(kvp.Key);
+					break;
+				}
+			}
+			if (oldSplit != _hoverSplit)
+			{
+				Invalidate();
+			}
+			base.OnMouseMove(e);
+		}
+
+		protected override void OnMouseLeave(EventArgs e)
+		{
+			MouseState = VisualState.Normal;
+			Invalidate();
+		}
+
+		protected override void OnMouseDown(MouseEventArgs e)
+		{
+			if (e.Button == MouseButtons.Left)
+			{
+				MouseState = VisualState.Pressed;
+
+				Rectangle trackRect = GetTrackRectangle();
+				Point mouse = new Point(e.X, e.Y);
+				for (int i = 0; i < Splits.Count; i++)
+				{
+					Rectangle thumbRect = _splitRects[Splits[i]];
+					if (thumbRect.Contains(mouse))
+					{
+						_selectedSplit = i;
+						_dragging = true;
+						Invalidate();
+						return;
+					}
+				}
+
+				if (_selectedSplit != -1 && trackRect.Contains(mouse))
+				{
+					int value = Splits[_selectedSplit];
+					Rectangle thumbRect = _splitRects[Splits[_selectedSplit]];
+					if (e.X < thumbRect.X && !Splits.Contains(value - 1))
+					{
+						Splits[_selectedSplit]--;
+						UpdateRects();
+					}
+					else if (e.X > thumbRect.Width && !Splits.Contains(value + 1))
+					{
+						Splits[_selectedSplit]++;
+						UpdateRects();
+					}
+				}
+			}
+		}
+		
+		protected override void OnMouseUp(MouseEventArgs e)
+		{
+			MouseState = VisualState.Hover;
+			if (_dragging)
+			{
+				_dragging = false;
+			}
+			Invalidate();
+		}
+
+		protected override void OnLostFocus(EventArgs e)
+		{
+			base.OnLostFocus(e);
+			Invalidate();
+		}
+
+		private Rectangle GetTrackRectangle()
+		{
+			return new Rectangle(ClientRectangle.X + MarkerSize, ClientRectangle.Y, ClientRectangle.Width - MarkerSize * 2, ClientRectangle.Height);
+		}
+
+		protected override void OnPaint(PaintEventArgs e)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			if (_colorSet == null)
+			{
+				_colorSet = skin.GetFieldColorSet(FieldType, SkinnedLightLevel.Normal);
+				_lightColorSet = skin.GetFieldColorSet(FieldType, SkinnedLightLevel.Light);
+			}
+
+			Graphics g = e.Graphics;
+
+			Color backColor = DesignMode ? SystemColors.Control : this.GetSkinnedPanelBackColor();
+			g.Clear(backColor);
+
+			g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
+
+			//trackbar
+			Rectangle trackRect = new Rectangle(ClientRectangle.X + MarkerSize, ClientRectangle.Y, ClientRectangle.Width - MarkerSize * 2, ClientRectangle.Height);
+
+			using (Pen pen = new Pen(Enabled ? _lightColorSet.Normal : _lightColorSet.Disabled, 2))
+			{
+				using (Pen forePen = new Pen(DesignMode ? ForeColor : this.GetSkinnedPanelForeColor()))
+				{
+					using (Brush foreBrush = new SolidBrush(forePen.Color))
+					{
+						g.DrawLine(pen, trackRect.X, trackRect.Y + trackRect.Height / 2, trackRect.Right, trackRect.Y + trackRect.Height / 2);
+
+						//tick marks
+						for (int i = Minimum; i <= Maximum; i++)
+						{
+							float p = (i - Minimum) / (float)(Maximum - Minimum);
+							int tickLeft = (int)(trackRect.Left + p * trackRect.Width);
+							g.DrawLine(forePen, tickLeft, trackRect.Bottom - 5, tickLeft, trackRect.Bottom - 1);
+							if (i == Minimum || i == Maximum || Splits.Contains(i))
+							{
+								g.DrawString(i.ToString(), _font, foreBrush, tickLeft - 3, trackRect.Top + 1);
+							}
+						}
+					}
+
+					ColorSet thumbSet = skin.GetWidgetColorSet(FieldType);
+
+					for (int i = 0; i < Splits.Count; i++)
+					{
+						Rectangle thumbRect;
+						if (_splitRects.TryGetValue(Splits[i], out thumbRect))
+						{
+							SolidBrush filledBrush = _lightColorSet.GetBrush(VisualState.Normal, false, Enabled);
+							if (_hoverSplit == i)
+							{
+								filledBrush = thumbSet.GetBrush(VisualState.Hover, Focused, Enabled);
+							}
+							else if (_selectedSplit == i)
+							{
+								filledBrush = thumbSet.GetBrush(VisualState.Focused, true, Enabled);
+							}
+							//thumb
+							g.FillEllipse(filledBrush, thumbRect);
+						}
+					}
+				}
+			}
+
+			g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
+
+			if (Focused)
+			{
+				SkinManager.Instance.DrawFocusRectangle(g, ClientRectangle);
+			}
+		}
+
+		private void mnuOptions_Opening(object sender, CancelEventArgs e)
+		{
+			int splitPt = GetValueUnderCursor();
+			if (splitPt == -1)
+			{
+				e.Cancel = true;
+				return;
+			}
+
+			if (_splitRects.ContainsKey(splitPt))
+			{
+				tsRemove.Tag = splitPt;
+				tsAdd.Visible = false;
+				tsRemove.Visible = true;
+				return;
+			}
+			tsAdd.Tag = splitPt;
+			tsAdd.Visible = true;
+			tsRemove.Visible = false;
+		}
+
+		private int GetValueUnderCursor()
+		{
+			Point pt = PointToClient(MousePosition);
+			Rectangle trackRect = new Rectangle(ClientRectangle.X + MarkerSize, ClientRectangle.Y, ClientRectangle.Width - MarkerSize * 2, ClientRectangle.Height);
+
+			if (!trackRect.Contains(pt))
+			{
+				return -1;
+			}
+
+			float pct = (pt.X - trackRect.Left) / (float)trackRect.Width;
+			return (int)(pct * (Maximum - Minimum)) + Minimum + 1;
+		}
+
+		private void TsRemove_Click(object sender, EventArgs e)
+		{
+			int split = (int)(sender as ToolStripMenuItem).Tag;
+			if (_splitRects.ContainsKey(split))
+			{
+				Splits.Remove(split);
+				_selectedSplit = -1;
+			}
+		}
+
+		private void TsAdd_Click(object sender, EventArgs e)
+		{
+			int split = (int)(sender as ToolStripMenuItem).Tag;
+			for (int i = 0; i < Splits.Count; i++)
+			{
+				if(split < Splits[i])
+				{
+					Splits.Insert(i, split);
+					_selectedSplit = i;
+					return;
+				}
+			}
+			Splits.Add(split);
+			_selectedSplit = Splits.Count - 1;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/IntervalSlider.resx b/editor source/SPNATI Character Editor/Controls/IntervalSlider.resx
new file mode 100644
index 0000000000000000000000000000000000000000..03c23f4e8eb2573ce687907dd2e5380cc7dbe91f
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/IntervalSlider.resx	
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="mnuOptions.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/LiveEpilogueEditor.Designer.cs b/editor source/SPNATI Character Editor/Controls/LiveEpilogueEditor.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..97e7649eea5d600ac571c4930d03ca9e6ca35707
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/LiveEpilogueEditor.Designer.cs	
@@ -0,0 +1,601 @@
+namespace SPNATI_Character_Editor.Controls
+{
+	partial class LiveEpilogueEditor
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.components = new System.ComponentModel.Container();
+			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+			this.splitContainer3 = new System.Windows.Forms.SplitContainer();
+			this.tsMainMenu = new System.Windows.Forms.ToolStrip();
+			this.toolStripLabel1 = new System.Windows.Forms.ToolStripLabel();
+			this.tsAddSprite = new System.Windows.Forms.ToolStripButton();
+			this.tsRemoveSprite = new System.Windows.Forms.ToolStripButton();
+			this.tsAddTransition = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
+			this.tsMoveUp = new System.Windows.Forms.ToolStripButton();
+			this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
+			this.lstScenes = new Desktop.CommonControls.RefreshableListBox();
+			this.tsToolbar = new System.Windows.Forms.ToolStrip();
+			this.tsAdd = new System.Windows.Forms.ToolStripDropDownButton();
+			this.addSpriteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.addSpeechBubbleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.addEmitterToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.addWaitForInputToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.tsRemove = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+			this.tsRefresh = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
+			this.tsAddKeyframe = new System.Windows.Forms.ToolStripButton();
+			this.tsRemoveKeyframe = new System.Windows.Forms.ToolStripButton();
+			this.tsAddEndFrame = new System.Windows.Forms.ToolStripButton();
+			this.tsFrameType = new System.Windows.Forms.ToolStripButton();
+			this.splitContainer2 = new System.Windows.Forms.SplitContainer();
+			this.splitContainer4 = new System.Windows.Forms.SplitContainer();
+			this.lblDataCaption = new Desktop.Skinning.SkinnedLabel();
+			this.table = new Desktop.CommonControls.PropertyTable();
+			this.tmrRealtime = new System.Windows.Forms.Timer(this.components);
+			this.timeline = new SPNATI_Character_Editor.EpilogueEditor.Timeline();
+			this.canvas = new SPNATI_Character_Editor.EpilogueEditor.LiveCanvas();
+			this.openFileDialog1 = new SPNATI_Character_Editor.Controls.CharacterImageDialog();
+			this.subTable = new Desktop.CommonControls.PropertyTable();
+			this.lblSubTable = new Desktop.Skinning.SkinnedLabel();
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+			this.splitContainer1.Panel1.SuspendLayout();
+			this.splitContainer1.Panel2.SuspendLayout();
+			this.splitContainer1.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).BeginInit();
+			this.splitContainer3.Panel1.SuspendLayout();
+			this.splitContainer3.Panel2.SuspendLayout();
+			this.splitContainer3.SuspendLayout();
+			this.tsMainMenu.SuspendLayout();
+			this.tsToolbar.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit();
+			this.splitContainer2.Panel1.SuspendLayout();
+			this.splitContainer2.Panel2.SuspendLayout();
+			this.splitContainer2.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer4)).BeginInit();
+			this.splitContainer4.Panel1.SuspendLayout();
+			this.splitContainer4.Panel2.SuspendLayout();
+			this.splitContainer4.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// splitContainer1
+			// 
+			this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.splitContainer1.Location = new System.Drawing.Point(0, 0);
+			this.splitContainer1.Name = "splitContainer1";
+			this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
+			// 
+			// splitContainer1.Panel1
+			// 
+			this.splitContainer1.Panel1.Controls.Add(this.splitContainer3);
+			// 
+			// splitContainer1.Panel2
+			// 
+			this.splitContainer1.Panel2.Controls.Add(this.splitContainer2);
+			this.splitContainer1.Size = new System.Drawing.Size(1131, 675);
+			this.splitContainer1.SplitterDistance = 175;
+			this.splitContainer1.TabIndex = 5;
+			// 
+			// splitContainer3
+			// 
+			this.splitContainer3.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.splitContainer3.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
+			this.splitContainer3.Location = new System.Drawing.Point(0, 0);
+			this.splitContainer3.Name = "splitContainer3";
+			// 
+			// splitContainer3.Panel1
+			// 
+			this.splitContainer3.Panel1.Controls.Add(this.tsMainMenu);
+			this.splitContainer3.Panel1.Controls.Add(this.lstScenes);
+			// 
+			// splitContainer3.Panel2
+			// 
+			this.splitContainer3.Panel2.Controls.Add(this.tsToolbar);
+			this.splitContainer3.Panel2.Controls.Add(this.timeline);
+			this.splitContainer3.Size = new System.Drawing.Size(1131, 175);
+			this.splitContainer3.SplitterDistance = 165;
+			this.splitContainer3.TabIndex = 0;
+			// 
+			// tsMainMenu
+			// 
+			this.tsMainMenu.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
+			this.tsMainMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+			this.toolStripLabel1,
+			this.tsAddSprite,
+			this.tsRemoveSprite,
+			this.tsAddTransition,
+			this.toolStripSeparator3,
+			this.tsMoveUp,
+			this.toolStripButton1});
+			this.tsMainMenu.Location = new System.Drawing.Point(0, 0);
+			this.tsMainMenu.Name = "tsMainMenu";
+			this.tsMainMenu.Size = new System.Drawing.Size(165, 25);
+			this.tsMainMenu.TabIndex = 7;
+			this.tsMainMenu.Text = "toolStrip1";
+			// 
+			// toolStripLabel1
+			// 
+			this.toolStripLabel1.Name = "toolStripLabel1";
+			this.toolStripLabel1.Size = new System.Drawing.Size(43, 22);
+			this.toolStripLabel1.Text = "Scenes";
+			// 
+			// tsAddSprite
+			// 
+			this.tsAddSprite.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsAddSprite.Image = global::SPNATI_Character_Editor.Properties.Resources.Add;
+			this.tsAddSprite.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsAddSprite.Name = "tsAddSprite";
+			this.tsAddSprite.Size = new System.Drawing.Size(23, 22);
+			this.tsAddSprite.Text = "Add Scene";
+			this.tsAddSprite.Click += new System.EventHandler(this.tsAddSprite_Click);
+			// 
+			// tsRemoveSprite
+			// 
+			this.tsRemoveSprite.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsRemoveSprite.Image = global::SPNATI_Character_Editor.Properties.Resources.Remove;
+			this.tsRemoveSprite.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsRemoveSprite.Name = "tsRemoveSprite";
+			this.tsRemoveSprite.Size = new System.Drawing.Size(23, 22);
+			this.tsRemoveSprite.Text = "Remove Scene";
+			this.tsRemoveSprite.Click += new System.EventHandler(this.tsRemoveSprite_Click);
+			// 
+			// tsAddTransition
+			// 
+			this.tsAddTransition.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsAddTransition.Image = global::SPNATI_Character_Editor.Properties.Resources.AddTransition;
+			this.tsAddTransition.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsAddTransition.Name = "tsAddTransition";
+			this.tsAddTransition.Size = new System.Drawing.Size(23, 22);
+			this.tsAddTransition.Text = "Add Transition";
+			this.tsAddTransition.Click += new System.EventHandler(this.tsAddTransition_Click);
+			// 
+			// toolStripSeparator3
+			// 
+			this.toolStripSeparator3.Name = "toolStripSeparator3";
+			this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
+			// 
+			// tsMoveUp
+			// 
+			this.tsMoveUp.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsMoveUp.Image = global::SPNATI_Character_Editor.Properties.Resources.UpArrow;
+			this.tsMoveUp.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsMoveUp.Name = "tsMoveUp";
+			this.tsMoveUp.Size = new System.Drawing.Size(23, 22);
+			this.tsMoveUp.Text = "Move Up";
+			this.tsMoveUp.Click += new System.EventHandler(this.tsMoveUp_Click);
+			// 
+			// toolStripButton1
+			// 
+			this.toolStripButton1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.toolStripButton1.Image = global::SPNATI_Character_Editor.Properties.Resources.DownArrow;
+			this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.toolStripButton1.Name = "toolStripButton1";
+			this.toolStripButton1.Size = new System.Drawing.Size(23, 22);
+			this.toolStripButton1.Text = "Move Down";
+			this.toolStripButton1.Click += new System.EventHandler(this.tsMoveDown_Click);
+			// 
+			// lstScenes
+			// 
+			this.lstScenes.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+			| System.Windows.Forms.AnchorStyles.Left)
+			| System.Windows.Forms.AnchorStyles.Right)));
+			this.lstScenes.BackColor = System.Drawing.Color.White;
+			this.lstScenes.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstScenes.ForeColor = System.Drawing.Color.Black;
+			this.lstScenes.FormattingEnabled = true;
+			this.lstScenes.IntegralHeight = false;
+			this.lstScenes.Location = new System.Drawing.Point(0, 25);
+			this.lstScenes.Margin = new System.Windows.Forms.Padding(0);
+			this.lstScenes.Name = "lstScenes";
+			this.lstScenes.Size = new System.Drawing.Size(165, 150);
+			this.lstScenes.TabIndex = 8;
+			this.lstScenes.SelectedIndexChanged += new System.EventHandler(this.lstScenes_SelectedIndexChanged);
+			// 
+			// tsToolbar
+			// 
+			this.tsToolbar.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
+			this.tsToolbar.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+			this.tsAdd,
+			this.tsRemove,
+			this.toolStripSeparator1,
+			this.tsRefresh,
+			this.toolStripSeparator2,
+			this.tsAddKeyframe,
+			this.tsRemoveKeyframe,
+			this.tsAddEndFrame,
+			this.tsFrameType});
+			this.tsToolbar.Location = new System.Drawing.Point(0, 0);
+			this.tsToolbar.Name = "tsToolbar";
+			this.tsToolbar.Size = new System.Drawing.Size(962, 25);
+			this.tsToolbar.TabIndex = 8;
+			this.tsToolbar.Text = "toolStrip1";
+			// 
+			// tsAdd
+			// 
+			this.tsAdd.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsAdd.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+			this.addSpriteToolStripMenuItem,
+			this.addSpeechBubbleToolStripMenuItem,
+			this.addEmitterToolStripMenuItem,
+			this.addWaitForInputToolStripMenuItem});
+			this.tsAdd.Image = global::SPNATI_Character_Editor.Properties.Resources.Add;
+			this.tsAdd.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsAdd.Name = "tsAdd";
+			this.tsAdd.Size = new System.Drawing.Size(29, 22);
+			this.tsAdd.Text = "Add Object";
+			// 
+			// addSpriteToolStripMenuItem
+			// 
+			this.addSpriteToolStripMenuItem.Name = "addSpriteToolStripMenuItem";
+			this.addSpriteToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D1)));
+			this.addSpriteToolStripMenuItem.Size = new System.Drawing.Size(237, 22);
+			this.addSpriteToolStripMenuItem.Text = "Add Sprite";
+			this.addSpriteToolStripMenuItem.Click += new System.EventHandler(this.addSpriteToolStripMenuItem_Click);
+			// 
+			// addSpeechBubbleToolStripMenuItem
+			// 
+			this.addSpeechBubbleToolStripMenuItem.Name = "addSpeechBubbleToolStripMenuItem";
+			this.addSpeechBubbleToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D2)));
+			this.addSpeechBubbleToolStripMenuItem.Size = new System.Drawing.Size(237, 22);
+			this.addSpeechBubbleToolStripMenuItem.Text = "Add Speech Bubble";
+			this.addSpeechBubbleToolStripMenuItem.Click += new System.EventHandler(this.addSpeechBubbleToolStripMenuItem_Click);
+			// 
+			// addEmitterToolStripMenuItem
+			// 
+			this.addEmitterToolStripMenuItem.Name = "addEmitterToolStripMenuItem";
+			this.addEmitterToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D3)));
+			this.addEmitterToolStripMenuItem.Size = new System.Drawing.Size(237, 22);
+			this.addEmitterToolStripMenuItem.Text = "Add Emitter";
+			this.addEmitterToolStripMenuItem.Click += new System.EventHandler(this.addEmitterToolStripMenuItem_Click);
+			// 
+			// addWaitForInputToolStripMenuItem
+			// 
+			this.addWaitForInputToolStripMenuItem.Name = "addWaitForInputToolStripMenuItem";
+			this.addWaitForInputToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Space)));
+			this.addWaitForInputToolStripMenuItem.Size = new System.Drawing.Size(237, 22);
+			this.addWaitForInputToolStripMenuItem.Text = "Add Wait for Input";
+			this.addWaitForInputToolStripMenuItem.Click += new System.EventHandler(this.addWaitForInputToolStripMenuItem_Click);
+			// 
+			// tsRemove
+			// 
+			this.tsRemove.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsRemove.Image = global::SPNATI_Character_Editor.Properties.Resources.Remove;
+			this.tsRemove.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsRemove.Name = "tsRemove";
+			this.tsRemove.Size = new System.Drawing.Size(23, 22);
+			this.tsRemove.Text = "Remove Scene";
+			this.tsRemove.Click += new System.EventHandler(this.tsRemove_Click);
+			// 
+			// toolStripSeparator1
+			// 
+			this.toolStripSeparator1.Name = "toolStripSeparator1";
+			this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
+			// 
+			// tsRefresh
+			// 
+			this.tsRefresh.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsRefresh.Image = global::SPNATI_Character_Editor.Properties.Resources.Refresh;
+			this.tsRefresh.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsRefresh.Name = "tsRefresh";
+			this.tsRefresh.Size = new System.Drawing.Size(23, 22);
+			this.tsRefresh.Text = "Refresh assets";
+			this.tsRefresh.ToolTipText = "Reload sprites from files";
+			this.tsRefresh.Click += new System.EventHandler(this.tsRefresh_Click);
+			// 
+			// toolStripSeparator2
+			// 
+			this.toolStripSeparator2.Name = "toolStripSeparator2";
+			this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
+			// 
+			// tsAddKeyframe
+			// 
+			this.tsAddKeyframe.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsAddKeyframe.Image = global::SPNATI_Character_Editor.Properties.Resources.AddKeyframe;
+			this.tsAddKeyframe.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsAddKeyframe.Name = "tsAddKeyframe";
+			this.tsAddKeyframe.Size = new System.Drawing.Size(23, 22);
+			this.tsAddKeyframe.Text = "Add Keyframe";
+			this.tsAddKeyframe.Click += new System.EventHandler(this.tsAddKeyframe_Click);
+			// 
+			// tsRemoveKeyframe
+			// 
+			this.tsRemoveKeyframe.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsRemoveKeyframe.Image = global::SPNATI_Character_Editor.Properties.Resources.RemoveKeyframe;
+			this.tsRemoveKeyframe.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsRemoveKeyframe.Name = "tsRemoveKeyframe";
+			this.tsRemoveKeyframe.Size = new System.Drawing.Size(23, 22);
+			this.tsRemoveKeyframe.Text = "Remove Keyframe";
+			this.tsRemoveKeyframe.Click += new System.EventHandler(this.tsRemoveKeyframe_Click);
+			// 
+			// tsAddEndFrame
+			// 
+			this.tsAddEndFrame.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsAddEndFrame.Image = global::SPNATI_Character_Editor.Properties.Resources.CopyKeyFrame;
+			this.tsAddEndFrame.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsAddEndFrame.Name = "tsAddEndFrame";
+			this.tsAddEndFrame.Size = new System.Drawing.Size(23, 22);
+			this.tsAddEndFrame.Text = "Copy first keyframe to end";
+			this.tsAddEndFrame.Click += new System.EventHandler(this.tsAddEndFrame_Click);
+			// 
+			// tsFrameType
+			// 
+			this.tsFrameType.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsFrameType.Image = global::SPNATI_Character_Editor.Properties.Resources.SplitKeyframe;
+			this.tsFrameType.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsFrameType.Name = "tsFrameType";
+			this.tsFrameType.Size = new System.Drawing.Size(23, 22);
+			this.tsFrameType.Text = "Toggle keyframe type";
+			this.tsFrameType.Click += new System.EventHandler(this.tsFrameType_Click);
+			// 
+			// splitContainer2
+			// 
+			this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.splitContainer2.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
+			this.splitContainer2.Location = new System.Drawing.Point(0, 0);
+			this.splitContainer2.Name = "splitContainer2";
+			// 
+			// splitContainer2.Panel1
+			// 
+			this.splitContainer2.Panel1.Controls.Add(this.splitContainer4);
+			// 
+			// splitContainer2.Panel2
+			// 
+			this.splitContainer2.Panel2.Controls.Add(this.canvas);
+			this.splitContainer2.Size = new System.Drawing.Size(1131, 496);
+			this.splitContainer2.SplitterDistance = 376;
+			this.splitContainer2.TabIndex = 0;
+			// 
+			// splitContainer4
+			// 
+			this.splitContainer4.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.splitContainer4.Location = new System.Drawing.Point(0, 0);
+			this.splitContainer4.Name = "splitContainer4";
+			this.splitContainer4.Orientation = System.Windows.Forms.Orientation.Horizontal;
+			// 
+			// splitContainer4.Panel1
+			// 
+			this.splitContainer4.Panel1.Controls.Add(this.table);
+			this.splitContainer4.Panel1.Controls.Add(this.lblDataCaption);
+			// 
+			// splitContainer4.Panel2
+			// 
+			this.splitContainer4.Panel2.Controls.Add(this.subTable);
+			this.splitContainer4.Panel2.Controls.Add(this.lblSubTable);
+			this.splitContainer4.Size = new System.Drawing.Size(376, 496);
+			this.splitContainer4.SplitterDistance = 248;
+			this.splitContainer4.TabIndex = 8;
+			// 
+			// lblDataCaption
+			// 
+			this.lblDataCaption.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+			| System.Windows.Forms.AnchorStyles.Left)
+			| System.Windows.Forms.AnchorStyles.Right)));
+			this.lblDataCaption.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.lblDataCaption.ForeColor = System.Drawing.Color.Blue;
+			this.lblDataCaption.Highlight = Desktop.Skinning.SkinnedHighlight.Heading;
+			this.lblDataCaption.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.lblDataCaption.Location = new System.Drawing.Point(3, 0);
+			this.lblDataCaption.Name = "lblDataCaption";
+			this.lblDataCaption.Size = new System.Drawing.Size(370, 25);
+			this.lblDataCaption.TabIndex = 7;
+			this.lblDataCaption.Text = "Data";
+			this.lblDataCaption.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+			// 
+			// table
+			// 
+			this.table.AllowDelete = false;
+			this.table.AllowFavorites = false;
+			this.table.AllowHelp = true;
+			this.table.AllowMacros = false;
+			this.table.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+			| System.Windows.Forms.AnchorStyles.Left)
+			| System.Windows.Forms.AnchorStyles.Right)));
+			this.table.BackColor = System.Drawing.Color.White;
+			this.table.Data = null;
+			this.table.Enabled = false;
+			this.table.HeaderType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.table.HideAddField = true;
+			this.table.HideSpeedButtons = true;
+			this.table.Location = new System.Drawing.Point(3, 25);
+			this.table.Margin = new System.Windows.Forms.Padding(0);
+			this.table.ModifyingProperty = null;
+			this.table.Name = "table";
+			this.table.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.table.PlaceholderText = null;
+			this.table.PreserveControls = true;
+			this.table.PreviewData = null;
+			this.table.RemoveCaption = "Remove";
+			this.table.RowHeaderWidth = 0F;
+			this.table.RunInitialAddEvents = false;
+			this.table.Size = new System.Drawing.Size(370, 223);
+			this.table.Sorted = true;
+			this.table.TabIndex = 6;
+			this.table.UndoManager = null;
+			this.table.UseAutoComplete = true;
+			// 
+			// tmrRealtime
+			// 
+			this.tmrRealtime.Interval = 30;
+			this.tmrRealtime.Tick += new System.EventHandler(this.tmrRealtime_Tick);
+			// 
+			// timeline
+			// 
+			this.timeline.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+			| System.Windows.Forms.AnchorStyles.Left)
+			| System.Windows.Forms.AnchorStyles.Right)));
+			this.timeline.CommandHistory = null;
+			this.timeline.CurrentTime = 0F;
+			this.timeline.ElapsedTime = 0F;
+			this.timeline.Enabled = false;
+			this.timeline.Location = new System.Drawing.Point(0, 25);
+			this.timeline.Name = "timeline";
+			this.timeline.PauseOnBreaks = false;
+			this.timeline.PlaybackAwaitingInput = false;
+			this.timeline.PlaybackTime = 0F;
+			this.timeline.Size = new System.Drawing.Size(962, 150);
+			this.timeline.TabIndex = 1;
+			// 
+			// canvas
+			// 
+			this.canvas.AllowZoom = true;
+			this.canvas.CustomDraw = false;
+			this.canvas.DisallowEdit = false;
+			this.canvas.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.canvas.Enabled = false;
+			this.canvas.Location = new System.Drawing.Point(0, 0);
+			this.canvas.Name = "canvas";
+			this.canvas.Size = new System.Drawing.Size(751, 496);
+			this.canvas.TabIndex = 0;
+			// 
+			// openFileDialog1
+			// 
+			this.openFileDialog1.Filter = "";
+			this.openFileDialog1.IncludeOpponents = false;
+			this.openFileDialog1.UseAbsolutePaths = false;
+			// 
+			// subTable
+			// 
+			this.subTable.AllowDelete = false;
+			this.subTable.AllowFavorites = false;
+			this.subTable.AllowHelp = true;
+			this.subTable.AllowMacros = false;
+			this.subTable.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+			| System.Windows.Forms.AnchorStyles.Left)
+			| System.Windows.Forms.AnchorStyles.Right)));
+			this.subTable.BackColor = System.Drawing.Color.White;
+			this.subTable.Data = null;
+			this.subTable.Enabled = false;
+			this.subTable.HeaderType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.subTable.HideAddField = true;
+			this.subTable.HideSpeedButtons = true;
+			this.subTable.Location = new System.Drawing.Point(3, 23);
+			this.subTable.Margin = new System.Windows.Forms.Padding(0);
+			this.subTable.ModifyingProperty = null;
+			this.subTable.Name = "subTable";
+			this.subTable.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.subTable.PlaceholderText = null;
+			this.subTable.PreserveControls = true;
+			this.subTable.PreviewData = null;
+			this.subTable.RemoveCaption = "Remove";
+			this.subTable.RowHeaderWidth = 0F;
+			this.subTable.RunInitialAddEvents = false;
+			this.subTable.Size = new System.Drawing.Size(370, 223);
+			this.subTable.Sorted = true;
+			this.subTable.TabIndex = 8;
+			this.subTable.UndoManager = null;
+			this.subTable.UseAutoComplete = true;
+			// 
+			// lblSubTable
+			// 
+			this.lblSubTable.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+			| System.Windows.Forms.AnchorStyles.Left)
+			| System.Windows.Forms.AnchorStyles.Right)));
+			this.lblSubTable.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.lblSubTable.ForeColor = System.Drawing.Color.Blue;
+			this.lblSubTable.Highlight = Desktop.Skinning.SkinnedHighlight.Heading;
+			this.lblSubTable.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.lblSubTable.Location = new System.Drawing.Point(3, -2);
+			this.lblSubTable.Name = "lblSubTable";
+			this.lblSubTable.Size = new System.Drawing.Size(370, 25);
+			this.lblSubTable.TabIndex = 9;
+			this.lblSubTable.Text = "Data";
+			this.lblSubTable.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+			// 
+			// LiveEpilogueEditor
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.splitContainer1);
+			this.Name = "LiveEpilogueEditor";
+			this.Size = new System.Drawing.Size(1131, 675);
+			this.splitContainer1.Panel1.ResumeLayout(false);
+			this.splitContainer1.Panel2.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+			this.splitContainer1.ResumeLayout(false);
+			this.splitContainer3.Panel1.ResumeLayout(false);
+			this.splitContainer3.Panel1.PerformLayout();
+			this.splitContainer3.Panel2.ResumeLayout(false);
+			this.splitContainer3.Panel2.PerformLayout();
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit();
+			this.splitContainer3.ResumeLayout(false);
+			this.tsMainMenu.ResumeLayout(false);
+			this.tsMainMenu.PerformLayout();
+			this.tsToolbar.ResumeLayout(false);
+			this.tsToolbar.PerformLayout();
+			this.splitContainer2.Panel1.ResumeLayout(false);
+			this.splitContainer2.Panel2.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit();
+			this.splitContainer2.ResumeLayout(false);
+			this.splitContainer4.Panel1.ResumeLayout(false);
+			this.splitContainer4.Panel2.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer4)).EndInit();
+			this.splitContainer4.ResumeLayout(false);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private SPNATI_Character_Editor.EpilogueEditor.LiveCanvas canvas;
+		private System.Windows.Forms.SplitContainer splitContainer1;
+		private System.Windows.Forms.SplitContainer splitContainer2;
+		private Desktop.CommonControls.RefreshableListBox lstScenes;
+		private System.Windows.Forms.ToolStrip tsMainMenu;
+		private System.Windows.Forms.ToolStripLabel toolStripLabel1;
+		private System.Windows.Forms.ToolStripButton tsAddSprite;
+		private System.Windows.Forms.ToolStripButton tsRemoveSprite;
+		private System.Windows.Forms.SplitContainer splitContainer3;
+		private Desktop.CommonControls.PropertyTable table;
+		private SPNATI_Character_Editor.EpilogueEditor.Timeline timeline;
+		private System.Windows.Forms.ToolStrip tsToolbar;
+		private System.Windows.Forms.ToolStripButton tsRemove;
+		private System.Windows.Forms.ToolStripDropDownButton tsAdd;
+		private System.Windows.Forms.ToolStripMenuItem addSpriteToolStripMenuItem;
+		private System.Windows.Forms.ToolStripMenuItem addSpeechBubbleToolStripMenuItem;
+		private System.Windows.Forms.ToolStripMenuItem addEmitterToolStripMenuItem;
+		private CharacterImageDialog openFileDialog1;
+		private System.Windows.Forms.Timer tmrRealtime;
+		private Desktop.Skinning.SkinnedLabel lblDataCaption;
+		private System.Windows.Forms.ToolStripMenuItem addWaitForInputToolStripMenuItem;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
+		private System.Windows.Forms.ToolStripButton tsRefresh;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
+		private System.Windows.Forms.ToolStripButton tsAddKeyframe;
+		private System.Windows.Forms.ToolStripButton tsRemoveKeyframe;
+		private System.Windows.Forms.ToolStripButton tsAddEndFrame;
+		private System.Windows.Forms.ToolStripButton tsFrameType;
+		private System.Windows.Forms.ToolStripButton tsAddTransition;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
+		private System.Windows.Forms.ToolStripButton tsMoveUp;
+		private System.Windows.Forms.ToolStripButton toolStripButton1;
+		private System.Windows.Forms.SplitContainer splitContainer4;
+		private Desktop.CommonControls.PropertyTable subTable;
+		private Desktop.Skinning.SkinnedLabel lblSubTable;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/LiveEpilogueEditor.cs b/editor source/SPNATI Character Editor/Controls/LiveEpilogueEditor.cs
new file mode 100644
index 0000000000000000000000000000000000000000..66afc853cb4f5e7b296f6d99ade4edd625b47027
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/LiveEpilogueEditor.cs	
@@ -0,0 +1,695 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+using Desktop;
+using Desktop.CommonControls;
+using SPNATI_Character_Editor.Actions;
+using SPNATI_Character_Editor.EpilogueEditing;
+using SPNATI_Character_Editor.EpilogueEditor;
+
+namespace SPNATI_Character_Editor.Controls
+{
+	public partial class LiveEpilogueEditor : UserControl
+	{
+		private Character _character;
+		private Epilogue _epilogue;
+		private LiveScene _scene;
+		private Scene _sourceScene;
+		private SceneTransition _sceneTransition;
+		private UndoManager _history = new UndoManager();
+		private float _time;
+		private float _playbackTime;
+		private float _elapsedTime;
+		private bool _playing;
+		private ILabel _labelData;
+		private ILabel _sublabelData;
+		private DateTime _lastTick;
+		private bool _cameraLocked;
+		private float _savedTime;
+
+		public LiveEpilogueEditor()
+		{
+			InitializeComponent();
+			_history.CommandApplied += _history_CommandApplied;
+			timeline.CommandHistory = _history;
+			timeline.DataSelected += Timeline_DataSelected;
+			timeline.TimeChanged += Timeline_TimeChanged;
+			timeline.PlaybackTimeChanged += Timeline_PlaybackTimeChanged;
+			timeline.ElapsedTimeChanged += Timeline_ElapsedTimeChanged;
+			timeline.WidgetSelected += Timeline_WidgetSelected;
+			timeline.PlaybackChanged += Timeline_PlaybackChanged;
+			timeline.UIRequested += Timeline_UIRequested;
+
+			canvas.UndoManager = _history;
+			canvas.ObjectSelected += Canvas_ObjectSelected;
+			canvas.AddToolBarButton(Properties.Resources.VideoCamera, "Toggle scene preview", true, ToggleCamera);
+			canvas.AddToolBarButton(Properties.Resources.Play, "Play scene", true, TogglePlay);
+			canvas.CanvasClicked += Canvas_CanvasClicked;
+			canvas.CustomPaint += Canvas_CustomPaint;
+
+			table.RecordFilter = RecordFilter;
+			table.PropertyChanged += Table_PropertyChanged;
+			subTable.RecordFilter = SubRecordFilter;
+		}
+
+		private bool RecordFilter(PropertyRecord record)
+		{
+			if (table.Data is LiveKeyframe)
+			{
+				return FilterKeyframeProperty(table, record);
+			}
+			else if (table.Data is LiveObject)
+			{
+				return FilterObjectProperty(table, record);
+			}
+			else if (table.Data is Scene && _sceneTransition != null)
+			{
+				return record.Key == "effect" || record.Key == "ease" || record.Key == "time" || record.Key == "color";
+			}
+			return true;
+		}
+
+		private bool SubRecordFilter(PropertyRecord record)
+		{
+			if (subTable.Data is LiveKeyframe)
+			{
+				return FilterKeyframeProperty(subTable, record);
+			}
+			else if (subTable.Data is LiveObject)
+			{
+				return FilterObjectProperty(subTable, record);
+			}
+			else if (subTable.Data is Scene && _sceneTransition != null)
+			{
+				return record.Key == "effect" || record.Key == "ease" || record.Key == "time" || record.Key == "color";
+			}
+			return true;
+		}
+
+		private bool FilterKeyframeProperty(PropertyTable dataTable, PropertyRecord record)
+		{
+			LiveKeyframe keyframe = dataTable.Data as LiveKeyframe;
+			return keyframe.FilterRecord(record);
+		}
+
+		private bool FilterObjectProperty(PropertyTable dataTable, PropertyRecord record)
+		{
+			LiveObject obj = dataTable.Data as LiveObject;
+			return obj.FilterRecord(record.Key);
+		}
+
+		private void Canvas_CanvasClicked(object sender, System.EventArgs e)
+		{
+			timeline.ResumePlayback();
+		}
+
+		private void ToggleCamera(ToolStripButton btn)
+		{
+			_cameraLocked = btn.Checked;
+			ToggleCamera(_cameraLocked);
+		}
+
+		private void ToggleCamera(bool locked)
+		{
+			_scene.LockToCamera = locked;
+			canvas.AllowZoom = !locked;
+			canvas.FitScreen();
+			canvas.InvalidateCanvas();
+		}
+
+		private void TogglePlay(ToolStripButton button)
+		{
+			bool enabled = button.Checked;
+			if (enabled)
+			{
+				_savedTime = _time;
+				canvas.DisallowEdit = true;
+				canvas.LockToolbar(true, button);
+				button.Image = Properties.Resources.Stop;
+				splitContainer1.Panel1Collapsed = true;
+				splitContainer2.Panel1Collapsed = true;
+				ToggleCamera(true);
+				timeline.PauseOnBreaks = true;
+				timeline.CurrentTime = 0;
+				timeline.EnablePlayback(true);
+			}
+			else
+			{
+				button.Image = Properties.Resources.Play;
+				canvas.DisallowEdit = false;
+				canvas.LockToolbar(false, button);
+				splitContainer1.Panel1Collapsed = false;
+				splitContainer2.Panel1Collapsed = false;
+				ToggleCamera(_cameraLocked);
+				timeline.PauseOnBreaks = false;
+				timeline.EnablePlayback(false);
+				timeline.CurrentTime = _savedTime;
+			}
+		}
+
+		private void _history_CommandApplied(object sender, CommandEventArgs e)
+		{
+			UpdateToolbar();
+		}
+
+		private void Timeline_PlaybackChanged(object sender, bool enabled)
+		{
+			_playing = enabled;
+			canvas.SetPlayback(enabled);
+			if (!_playing)
+			{
+				UpdateToolbar();
+			}
+		}
+
+		public void SetActive(bool active)
+		{
+			_lastTick = DateTime.Now;
+			tmrRealtime.Enabled = active;
+		}
+
+		private void Canvas_ObjectSelected(object sender, CanvasSelectionArgs args)
+		{
+			timeline.SelectWidgetWithData(args.Object);
+		}
+
+		private void Timeline_WidgetSelected(object sender, ITimelineObject widget)
+		{
+			canvas.SelectData(widget?.GetData());
+		}
+
+		private void Timeline_PlaybackTimeChanged(object sender, float time)
+		{
+			_playbackTime = time;
+			canvas.UpdateTime(_time, _playbackTime, _elapsedTime);
+		}
+
+		private void Timeline_ElapsedTimeChanged(object sender, float time)
+		{
+			_elapsedTime = time;
+			canvas.UpdateTime(_time, _playbackTime, _elapsedTime);
+		}
+
+		private void Timeline_TimeChanged(object sender, float time)
+		{
+			_time = time;
+			canvas.UpdateTime(time, _playbackTime, _elapsedTime);
+			UpdateToolbar();
+		}
+
+		public void SetEpilogue(Character character, Epilogue epilogue)
+		{
+			_character = character;
+			SaveScene();
+			_scene = null;
+			_sourceScene = null;
+			_epilogue = epilogue;
+			lstScenes.Items.Clear();
+			if (epilogue != null)
+			{
+				foreach (Scene scene in epilogue.Scenes)
+				{
+					lstScenes.Items.Add(scene);
+				}
+			}
+			if (lstScenes.Items.Count > 0)
+			{
+				lstScenes.SelectedIndex = 0;
+			}
+		}
+
+		public void Destroy()
+		{
+			LiveImageCache.Clear();
+		}
+
+		private void Timeline_DataSelected(object sender, DataSelectionArgs data)
+		{
+			if (data.Data == null && data.PreviewData == null)
+			{
+				SetTableData(null, null);
+				SetSubTableData(null, null);
+			}
+			if (data.Data is LiveKeyframe || data.PreviewData is LiveKeyframe)
+			{
+				//when selecting a keyframe, also select the object
+				LiveKeyframe kf = data.Data as LiveKeyframe ?? data.PreviewData as LiveKeyframe;
+				SetTableData(kf.Data, null);
+				SetSubTableData(data.Data, data.PreviewData);
+			}
+			else
+			{
+				//when selecting something else, clear the sub-table if it doesn't belong to that object, or open the current frame
+				LiveKeyframe kf = subTable.Data as LiveKeyframe;
+				if (kf != null && kf.Data != data.Data)
+				{
+					SetSubTableData(null, null);
+				}
+			}
+			UpdateToolbar();
+		}
+
+		private void SetTableData(object data, object previewData)
+		{
+			if (_labelData != null)
+			{
+				_labelData.LabelChanged -= _labelData_LabelChanged;
+				_labelData = null;
+			}
+			table.SetDataAsync(data, previewData);
+			_labelData = data as ILabel;
+			if (_labelData != null)
+			{
+				_labelData.LabelChanged += _labelData_LabelChanged;
+				lblDataCaption.Text = _labelData.GetLabel();
+			}
+			else
+			{
+				lblDataCaption.Text = data?.ToString();
+			}
+		}
+		private void SetSubTableData(object data, object previewData)
+		{
+			if (_sublabelData != null)
+			{
+				_sublabelData.LabelChanged -= _labelData_LabelChanged;
+				_sublabelData = null;
+			}
+			subTable.SetDataAsync(data, previewData);
+			_sublabelData = data as ILabel;
+			if (_sublabelData != null)
+			{
+				_sublabelData.LabelChanged += _labelData_LabelChanged;
+				lblSubTable.Text = _sublabelData.GetLabel();
+			}
+			else
+			{
+				lblSubTable.Text = data?.ToString();
+			}
+		}
+
+		private void _labelData_LabelChanged(object sender, System.EventArgs e)
+		{
+			if (_labelData != null)
+			{
+				lblDataCaption.Text = _labelData.GetLabel();
+				if (_labelData == _scene)
+				{
+					_sourceScene.Name = _scene.Name;
+					lstScenes.RefreshListItems();
+				}
+			}
+			if (_sublabelData != null)
+			{
+				lblSubTable.Text = _sublabelData.GetLabel();
+			}
+		}
+
+		private void SaveScene()
+		{
+			if (_scene == null) { return; }
+		}
+
+		private void lstScenes_SelectedIndexChanged(object sender, System.EventArgs e)
+		{
+			Scene scene = lstScenes.SelectedItem as Scene;
+			SetScene(scene);
+		}
+
+		private void SetScene(Scene newScene)
+		{
+			if (_sourceScene == newScene)
+			{
+				if (_sourceScene.Transition) { return; }
+				SetTableData(_scene, null);
+				return;
+			}
+			SaveScene();
+			_sceneTransition = null;
+			if (_scene != null)
+			{
+				_scene.PropertyChanged -= _scene_PropertyChanged;
+			}
+			_sourceScene = newScene;
+			_scene = null;
+			if (_sourceScene != null)
+			{
+				if (_sourceScene.Transition)
+				{
+					_sceneTransition = new SceneTransition(_sourceScene, canvas.CanvasWidth, canvas.CanvasHeight);
+					_scene = null;
+					canvas.CustomDraw = true;
+				}
+				else
+				{
+					canvas.CustomDraw = false;
+					_scene = new LiveScene(_sourceScene, _character);
+					_scene.PropertyChanged += _scene_PropertyChanged;
+				}
+			}
+			bool enabled = _scene != null;
+			EpilogueContext context = new EpilogueContext(_character, _epilogue, _sourceScene);
+			context.Context = CharacterContext.Pose;
+			table.Context = context;
+			subTable.Context = context;
+			canvas.SetData(_character, _scene);
+			timeline.SetData(_scene);
+			object data = _scene;
+			if (_sceneTransition != null)
+			{
+				data = _sourceScene;
+			}
+			SetTableData(data, null);
+			tsToolbar.Enabled = enabled;
+			canvas.Enabled = enabled;
+			table.Enabled = data != null;
+			subTable.Enabled = data != null;
+			timeline.Enabled = enabled;
+			canvas.FitScreen();
+		}
+
+		private void _scene_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			if (e.PropertyName == "Name")
+			{
+				_sourceScene.Name = _scene.Name;
+				lstScenes.RefreshListItems();
+			}
+		}
+
+		private void UpdateToolbar()
+		{
+			if (_playing) { return; }
+			KeyframedWidget selectedWidget = timeline.SelectedObject as KeyframedWidget;
+
+			tsRemoveSprite.Enabled = tsAddEndFrame.Enabled = (selectedWidget != null);
+			tsAddKeyframe.Enabled = false;
+			tsRemoveKeyframe.Enabled = false;
+			tsFrameType.Enabled = false;
+			if (selectedWidget != null)
+			{
+				tsRemoveSprite.Enabled = true;
+				LiveKeyframe kf = selectedWidget.Data.Keyframes.Find(k => k.Time == _time);
+				if (kf == null)
+				{
+					float start = selectedWidget.GetStart();
+					if (_time > start)
+					{
+						tsAddKeyframe.Enabled = true;
+					}
+				}
+				if (selectedWidget.SelectedFrame != null && selectedWidget.SelectedFrame.Time != 0)
+				{
+					tsRemoveKeyframe.Enabled = true;
+					tsFrameType.Enabled = true;
+				}
+			}
+		}
+
+		private void addSpriteToolStripMenuItem_Click(object sender, System.EventArgs e)
+		{
+			openFileDialog1.UseAbsolutePaths = true;
+			if (openFileDialog1.ShowDialog(_character, "") == DialogResult.OK)
+			{
+				string src = openFileDialog1.FileName;
+				LiveSprite sprite = _scene.AddSprite(_time, src);
+				sprite.AddValue<string>(0, "Src", src);
+
+				string id = Path.GetFileNameWithoutExtension(src);
+				int hyphen = id.IndexOf('-');
+				if (hyphen == 1 || hyphen == 2)
+				{
+					id = id.Substring(hyphen + 1);
+				}
+				sprite.Id = _scene.GetUniqueId(id);
+				timeline.SelectObject(timeline.CreateWidget(sprite));
+			}
+		}
+
+		private void addSpeechBubbleToolStripMenuItem_Click(object sender, System.EventArgs e)
+		{
+			LiveBubble bubble = _scene.AddBubble(_time);
+			timeline.SelectObject(timeline.CreateWidget(bubble));
+		}
+
+		private void addWaitForInputToolStripMenuItem_Click(object sender, EventArgs e)
+		{
+			if (_time == 0) { return; }
+			ITimelineBreak brk = _scene.AddBreak(_time);
+			if (brk != null)
+			{
+				timeline.AddBreak(brk);
+				timeline.CurrentTime = brk.Time;
+			}
+		}
+
+		private void addEmitterToolStripMenuItem_Click(object sender, System.EventArgs e)
+		{
+			LiveEmitter emitter = _scene.AddEmitter(_time);
+			timeline.SelectObject(timeline.CreateWidget(emitter));
+		}
+
+		private void tmrRealtime_Tick(object sender, System.EventArgs e)
+		{
+			DateTime now = DateTime.Now;
+			float elapsedSec = (float)(now - _lastTick).TotalSeconds;
+			float elapsed = elapsedSec * 1000;
+			if (_sceneTransition != null)
+			{
+				_sceneTransition.Update(elapsedSec);
+				canvas.InvalidateCanvas();
+			}
+			else
+			{
+				_scene?.UpdateRealTime(elapsed, canvas.Playing);
+			}
+			_lastTick = now;
+		}
+
+		private void Timeline_UIRequested(object sender, object data)
+		{
+			LiveSpriteKeyframe frame = data as LiveSpriteKeyframe;
+			if (frame != null)
+			{
+				openFileDialog1.UseAbsolutePaths = true;
+				if (openFileDialog1.ShowDialog(_character, frame.Src ?? "") == DialogResult.OK)
+				{
+					string src = openFileDialog1.FileName;
+					frame.Src = src;
+				}
+			}
+		}
+
+		private void tsRefresh_Click(object sender, EventArgs e)
+		{
+			LiveImageCache.Refresh();
+			foreach (LiveObject obj in _scene.Tracks)
+			{
+				if (obj is LiveSprite)
+				{
+					LiveSprite sprite = obj as LiveSprite;
+					if (!string.IsNullOrEmpty(sprite.Src))
+					{
+						sprite.Image = LiveImageCache.Get(sprite.Src);
+						if (sprite.Image != null)
+						{
+							sprite.Width = sprite.Image.Width;
+							sprite.Height = sprite.Image.Height;
+						}
+						else
+						{
+							sprite.Width = 100;
+							sprite.Height = 100;
+						}
+					}
+				}
+			}
+			canvas.InvalidateCanvas();
+		}
+
+		private void tsAddKeyframe_Click(object sender, EventArgs e)
+		{
+			if (timeline.SelectedObject == null) { return; }
+			KeyframedWidget widget = timeline.SelectedObject as KeyframedWidget;
+			//TODO: Make this a command
+			LiveKeyframe frame = widget.Data.AddKeyframe(_time - widget.GetStart());
+			widget.SelectKeyframe(frame, null, false);
+			UpdateToolbar();
+		}
+
+		private void tsRemoveKeyframe_Click(object sender, EventArgs e)
+		{
+			if (timeline.SelectedObject == null) { return; }
+			KeyframedWidget widget = timeline.SelectedObject as KeyframedWidget;
+			LiveKeyframe frame = widget.SelectedFrame;
+			if (frame != null)
+			{
+				DeleteKeyframeCommand command = new DeleteKeyframeCommand(widget.Data, frame);
+				_history.Commit(command);
+			}
+			UpdateToolbar();
+		}
+
+		private void tsAddEndFrame_Click(object sender, EventArgs e)
+		{
+			if (timeline.SelectedObject == null) { return; }
+			KeyframedWidget widget = timeline.SelectedObject as KeyframedWidget;
+			LiveAnimatedObject sprite = widget.Data;
+			if (sprite.Keyframes.Count > 0)
+			{
+				LiveKeyframe kf = widget.Data.Keyframes[0];
+				kf = sprite.CopyKeyframe(kf, new HashSet<string>());
+				PasteKeyframeCommand command = new PasteKeyframeCommand(sprite, kf, widget.GetEnd(timeline.Duration));
+				_history.Commit(command);
+				timeline.CurrentTime = command.NewKeyframe.Time;
+			}
+			UpdateToolbar();
+		}
+
+		private void tsFrameType_Click(object sender, EventArgs e)
+		{
+			if (timeline.SelectedObject == null) { return; }
+			KeyframedWidget widget = timeline.SelectedObject as KeyframedWidget;
+			LiveKeyframe frame = widget.SelectedFrame;
+			if (frame != null)
+			{
+				HashSet<string> props = new HashSet<string>();
+				foreach (string p in widget.SelectedProperties)
+				{
+					props.Add(p);
+				}
+				ToggleKeyframeTypeCommand command = new ToggleKeyframeTypeCommand(widget.Data, frame, props);
+				_history.Commit(command);
+			}
+			UpdateToolbar();
+		}
+
+		private void tsRemove_Click(object sender, EventArgs e)
+		{
+			if (timeline.SelectedObject == null) { return; }
+			if (MessageBox.Show($"Are you sure you want to remove {timeline.SelectedObject.ToString()}?", "Remove Object", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
+			{
+				timeline.RemoveSelectedWidget();
+				UpdateToolbar();
+			}
+		}
+
+		private void Canvas_CustomPaint(object sender, CanvasPaintArgs e)
+		{
+			Graphics g = e.Graphics;
+			_sceneTransition.Draw(g, e.Width, e.Height);
+		}
+
+		private void Table_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			if (_sceneTransition == null)
+			{
+				return;
+			}
+
+			_sceneTransition = new SceneTransition(_sourceScene, canvas.CanvasWidth, canvas.CanvasHeight);
+		}
+
+		private void tsAddSprite_Click(object sender, EventArgs e)
+		{
+			AddScene();
+		}
+
+		private void tsAddTransition_Click(object sender, EventArgs e)
+		{
+			AddSceneTransition();
+		}
+
+		private void AddScene()
+		{
+			Scene scene = _sourceScene;
+
+			Scene newScene = new Scene(100, 100);
+			if (scene != null)
+			{
+				int index = _epilogue.Scenes.IndexOf(scene);
+				lstScenes.Items.Insert(index + 1, newScene);
+				_epilogue.Scenes.Insert(index + 1, newScene);
+			}
+			else
+			{
+				_epilogue.Scenes.Add(newScene);
+				lstScenes.Items.Add(newScene);
+			}
+			lstScenes.SelectedItem = newScene;
+		}
+
+		private void AddSceneTransition()
+		{
+			Scene scene = _sourceScene;
+
+			Scene transition = new Scene(true)
+			{
+				Transition = true
+			};
+
+			if (scene != null)
+			{
+				int index = _epilogue.Scenes.IndexOf(scene);
+				lstScenes.Items.Insert(index + 1, transition);
+				_epilogue.Scenes.Insert(index + 1, transition);
+			}
+			else
+			{
+				_epilogue.Scenes.Add(transition);
+				lstScenes.Items.Add(transition);
+			}
+			lstScenes.SelectedItem = transition;
+		}
+
+		private void tsRemoveSprite_Click(object sender, EventArgs e)
+		{
+			Scene scene = _sourceScene;
+			if (scene != null)
+			{
+				if (MessageBox.Show("Are you sure you want to permanently remove this scene?", "Remove Scene", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
+				{
+					int index = lstScenes.SelectedIndex;
+					_epilogue.Scenes.Remove(scene);
+					lstScenes.Items.Remove(scene);
+					index = Math.Min(index, lstScenes.Items.Count - 1);
+					lstScenes.SelectedIndex = index;
+				}
+			}
+		}
+
+		private void tsMoveUp_Click(object sender, EventArgs e)
+		{
+			if (_sourceScene == null) { return; }
+			int index = lstScenes.SelectedIndex;
+			if (index > 0)
+			{
+				lstScenes.SelectedIndexChanged -= lstScenes_SelectedIndexChanged;
+				lstScenes.Items.RemoveAt(index);
+				lstScenes.Items.Insert(index - 1, _sourceScene);
+				_epilogue.Scenes.RemoveAt(index);
+				_epilogue.Scenes.Insert(index - 1, _sourceScene);
+				lstScenes.SelectedIndex = index - 1;
+				lstScenes.SelectedIndexChanged += lstScenes_SelectedIndexChanged;
+			}
+		}
+
+		private void tsMoveDown_Click(object sender, EventArgs e)
+		{
+			if (_sourceScene == null) { return; }
+			int index = lstScenes.SelectedIndex;
+			if (index < lstScenes.Items.Count - 1)
+			{
+				lstScenes.SelectedIndexChanged -= lstScenes_SelectedIndexChanged;
+				lstScenes.Items.RemoveAt(index);
+				lstScenes.Items.Insert(index + 1, _sourceScene);
+				_epilogue.Scenes.RemoveAt(index);
+				_epilogue.Scenes.Insert(index + 1, _sourceScene);
+				lstScenes.SelectedIndex = index + 1;
+				lstScenes.SelectedIndexChanged += lstScenes_SelectedIndexChanged;
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/MarkerGrid.Designer.cs b/editor source/SPNATI Character Editor/Controls/MarkerGrid.Designer.cs
index 982b155165e1a9de111cd497a2d7ce505d8ccdc9..0f81df68c3f6f533d6ecffc1ec85f95df292403c 100644
--- a/editor source/SPNATI Character Editor/Controls/MarkerGrid.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/MarkerGrid.Designer.cs	
@@ -28,10 +28,11 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.gridMarkers = new System.Windows.Forms.DataGridView();
+			this.gridMarkers = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColName = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColScope = new System.Windows.Forms.DataGridViewComboBoxColumn();
+			this.ColScope = new Desktop.Skinning.SkinnedDataGridViewComboBoxColumn();
 			this.ColDescription = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.ColDelete = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
 			((System.ComponentModel.ISupportInitialize)(this.gridMarkers)).BeginInit();
 			this.SuspendLayout();
 			// 
@@ -42,13 +43,16 @@
 			this.gridMarkers.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColName,
             this.ColScope,
-            this.ColDescription});
+            this.ColDescription,
+            this.ColDelete});
 			this.gridMarkers.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.gridMarkers.Location = new System.Drawing.Point(0, 0);
 			this.gridMarkers.MultiSelect = false;
 			this.gridMarkers.Name = "gridMarkers";
 			this.gridMarkers.Size = new System.Drawing.Size(653, 362);
 			this.gridMarkers.TabIndex = 5;
+			this.gridMarkers.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridMarkers_CellContentClick);
+			this.gridMarkers.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.gridMarkers_CellPainting);
 			this.gridMarkers.SelectionChanged += new System.EventHandler(this.gridMarkers_SelectionChanged);
 			// 
 			// ColName
@@ -60,9 +64,6 @@
 			// ColScope
 			// 
 			this.ColScope.HeaderText = "Scope";
-			this.ColScope.Items.AddRange(new object[] {
-            "Private",
-            "Public"});
 			this.ColScope.Name = "ColScope";
 			this.ColScope.Width = 80;
 			// 
@@ -74,6 +75,12 @@
 			this.ColDescription.Resizable = System.Windows.Forms.DataGridViewTriState.True;
 			this.ColDescription.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
 			// 
+			// ColDelete
+			// 
+			this.ColDelete.HeaderText = "";
+			this.ColDelete.Name = "ColDelete";
+			this.ColDelete.Width = 21;
+			// 
 			// MarkerGrid
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -88,9 +95,10 @@
 
 		#endregion
 
-		private System.Windows.Forms.DataGridView gridMarkers;
+		private Desktop.Skinning.SkinnedDataGridView gridMarkers;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColName;
-		private System.Windows.Forms.DataGridViewComboBoxColumn ColScope;
+		private Desktop.Skinning.SkinnedDataGridViewComboBoxColumn ColScope;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColDescription;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColDelete;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/MarkerGrid.cs b/editor source/SPNATI Character Editor/Controls/MarkerGrid.cs
index 3cca649bb0dadc6c5de5a9dec050cd71866d14b5..587ec7498f7cf950f9adbe6df3b71354d960b953 100644
--- a/editor source/SPNATI Character Editor/Controls/MarkerGrid.cs	
+++ b/editor source/SPNATI Character Editor/Controls/MarkerGrid.cs	
@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Drawing;
 using System.Linq;
 using System.Windows.Forms;
 
@@ -21,11 +22,13 @@ namespace SPNATI_Character_Editor.Controls
 				{
 					gridMarkers.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
 					gridMarkers.AllowUserToDeleteRows = false;
+					ColDelete.Visible = false;
 				}
 				else
 				{
-					gridMarkers.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect;
-					gridMarkers.AllowUserToDeleteRows = true;
+					gridMarkers.SelectionMode = DataGridViewSelectionMode.CellSelect;
+					gridMarkers.AllowUserToDeleteRows = false;
+					ColDelete.Visible = true;
 				}
 				gridMarkers.ReadOnly = value;
 			}
@@ -34,6 +37,8 @@ namespace SPNATI_Character_Editor.Controls
 		public MarkerGrid()
 		{
 			InitializeComponent();
+			ColScope.Items.AddRange(new object[] { "Private", "Public" });
+			ColDelete.Flat = true;
 		}
 
 		public void SetCharacter(Character character)
@@ -112,5 +117,34 @@ namespace SPNATI_Character_Editor.Controls
 			}
 			SelectionChanged?.Invoke(this, marker);
 		}
+
+		private void gridMarkers_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
+		{
+			if (e.ColumnIndex == ColDelete.Index)
+			{
+				Image img = Properties.Resources.Delete;
+				e.Paint(e.CellBounds, DataGridViewPaintParts.All);
+				var w = img.Width;
+				var h = img.Height;
+				var x = e.CellBounds.Left + (e.CellBounds.Width - w) / 2;
+				var y = e.CellBounds.Top + (e.CellBounds.Height - h) / 2;
+
+				e.Graphics.DrawImage(img, new Rectangle(x, y, w, h));
+				e.Handled = true;
+			}
+		}
+
+		private void gridMarkers_CellContentClick(object sender, DataGridViewCellEventArgs e)
+		{
+			if (e.ColumnIndex < 0 || e.ColumnIndex >= gridMarkers.Columns.Count || e.RowIndex == gridMarkers.NewRowIndex || ReadOnly)
+			{
+				return;
+			}
+			DataGridViewColumn col = gridMarkers.Columns[e.ColumnIndex];
+			if (col == ColDelete)
+			{
+				gridMarkers.Rows.RemoveAt(e.RowIndex);
+			}
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/MarkerOptions.Designer.cs b/editor source/SPNATI Character Editor/Controls/MarkerOptions.Designer.cs
index fb309869a2d3b194e4653fbf29b6f9caa579dfb3..87348572e16236f15060a26367e3ea570ba80050 100644
--- a/editor source/SPNATI Character Editor/Controls/MarkerOptions.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/MarkerOptions.Designer.cs	
@@ -28,16 +28,16 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.groupBox1 = new System.Windows.Forms.GroupBox();
-			this.radClear = new System.Windows.Forms.RadioButton();
-			this.radDecrement = new System.Windows.Forms.RadioButton();
-			this.radIncrement = new System.Windows.Forms.RadioButton();
-			this.radSet = new System.Windows.Forms.RadioButton();
-			this.txtValue = new System.Windows.Forms.TextBox();
-			this.chkPerTarget = new System.Windows.Forms.CheckBox();
-			this.chkPersistent = new System.Windows.Forms.CheckBox();
-			this.lblMarker = new System.Windows.Forms.Label();
-			this.txtMarker = new System.Windows.Forms.TextBox();
+			this.groupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.radClear = new Desktop.Skinning.SkinnedRadioButton();
+			this.radDecrement = new Desktop.Skinning.SkinnedRadioButton();
+			this.radIncrement = new Desktop.Skinning.SkinnedRadioButton();
+			this.radSet = new Desktop.Skinning.SkinnedRadioButton();
+			this.txtValue = new Desktop.Skinning.SkinnedTextBox();
+			this.chkPerTarget = new Desktop.Skinning.SkinnedCheckBox();
+			this.chkPersistent = new Desktop.Skinning.SkinnedCheckBox();
+			this.lblMarker = new Desktop.Skinning.SkinnedLabel();
+			this.txtMarker = new Desktop.Skinning.SkinnedTextBox();
 			this.groupBox1.SuspendLayout();
 			this.SuspendLayout();
 			// 
@@ -52,7 +52,7 @@
 			this.groupBox1.Controls.Add(this.txtValue);
 			this.groupBox1.Location = new System.Drawing.Point(3, 29);
 			this.groupBox1.Name = "groupBox1";
-			this.groupBox1.Size = new System.Drawing.Size(190, 67);
+			this.groupBox1.Size = new System.Drawing.Size(190, 77);
 			this.groupBox1.TabIndex = 1;
 			this.groupBox1.TabStop = false;
 			this.groupBox1.Text = "Marker Operation";
@@ -60,7 +60,7 @@
 			// radClear
 			// 
 			this.radClear.AutoSize = true;
-			this.radClear.Location = new System.Drawing.Point(135, 20);
+			this.radClear.Location = new System.Drawing.Point(135, 27);
 			this.radClear.Name = "radClear";
 			this.radClear.Size = new System.Drawing.Size(49, 17);
 			this.radClear.TabIndex = 5;
@@ -72,7 +72,7 @@
 			// radDecrement
 			// 
 			this.radDecrement.AutoSize = true;
-			this.radDecrement.Location = new System.Drawing.Point(88, 45);
+			this.radDecrement.Location = new System.Drawing.Point(88, 52);
 			this.radDecrement.Name = "radDecrement";
 			this.radDecrement.Size = new System.Drawing.Size(77, 17);
 			this.radDecrement.TabIndex = 4;
@@ -84,7 +84,7 @@
 			// radIncrement
 			// 
 			this.radIncrement.AutoSize = true;
-			this.radIncrement.Location = new System.Drawing.Point(6, 45);
+			this.radIncrement.Location = new System.Drawing.Point(6, 52);
 			this.radIncrement.Name = "radIncrement";
 			this.radIncrement.Size = new System.Drawing.Size(72, 17);
 			this.radIncrement.TabIndex = 3;
@@ -96,7 +96,7 @@
 			// radSet
 			// 
 			this.radSet.AutoSize = true;
-			this.radSet.Location = new System.Drawing.Point(6, 20);
+			this.radSet.Location = new System.Drawing.Point(6, 27);
 			this.radSet.Name = "radSet";
 			this.radSet.Size = new System.Drawing.Size(44, 17);
 			this.radSet.TabIndex = 2;
@@ -109,7 +109,10 @@
 			// 
 			this.txtValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.txtValue.Location = new System.Drawing.Point(56, 19);
+			this.txtValue.BackColor = System.Drawing.Color.White;
+			this.txtValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtValue.ForeColor = System.Drawing.Color.Black;
+			this.txtValue.Location = new System.Drawing.Point(56, 26);
 			this.txtValue.Name = "txtValue";
 			this.txtValue.Size = new System.Drawing.Size(73, 20);
 			this.txtValue.TabIndex = 1;
@@ -117,7 +120,7 @@
 			// chkPerTarget
 			// 
 			this.chkPerTarget.AutoSize = true;
-			this.chkPerTarget.Location = new System.Drawing.Point(3, 102);
+			this.chkPerTarget.Location = new System.Drawing.Point(3, 112);
 			this.chkPerTarget.Name = "chkPerTarget";
 			this.chkPerTarget.Size = new System.Drawing.Size(76, 17);
 			this.chkPerTarget.TabIndex = 2;
@@ -127,7 +130,7 @@
 			// chkPersistent
 			// 
 			this.chkPersistent.AutoSize = true;
-			this.chkPersistent.Location = new System.Drawing.Point(91, 102);
+			this.chkPersistent.Location = new System.Drawing.Point(91, 112);
 			this.chkPersistent.Name = "chkPersistent";
 			this.chkPersistent.Size = new System.Drawing.Size(72, 17);
 			this.chkPersistent.TabIndex = 3;
@@ -137,6 +140,8 @@
 			// lblMarker
 			// 
 			this.lblMarker.AutoSize = true;
+			this.lblMarker.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblMarker.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.lblMarker.Location = new System.Drawing.Point(3, 6);
 			this.lblMarker.Name = "lblMarker";
 			this.lblMarker.Size = new System.Drawing.Size(43, 13);
@@ -145,6 +150,9 @@
 			// 
 			// txtMarker
 			// 
+			this.txtMarker.BackColor = System.Drawing.Color.White;
+			this.txtMarker.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtMarker.ForeColor = System.Drawing.Color.Black;
 			this.txtMarker.Location = new System.Drawing.Point(52, 3);
 			this.txtMarker.Name = "txtMarker";
 			this.txtMarker.Size = new System.Drawing.Size(141, 20);
@@ -160,7 +168,7 @@
 			this.Controls.Add(this.chkPerTarget);
 			this.Controls.Add(this.groupBox1);
 			this.Name = "MarkerOptions";
-			this.Size = new System.Drawing.Size(196, 122);
+			this.Size = new System.Drawing.Size(196, 132);
 			this.groupBox1.ResumeLayout(false);
 			this.groupBox1.PerformLayout();
 			this.ResumeLayout(false);
@@ -170,15 +178,15 @@
 
 		#endregion
 
-		private System.Windows.Forms.GroupBox groupBox1;
-		private System.Windows.Forms.RadioButton radClear;
-		private System.Windows.Forms.RadioButton radDecrement;
-		private System.Windows.Forms.RadioButton radIncrement;
-		private System.Windows.Forms.RadioButton radSet;
-		private System.Windows.Forms.TextBox txtValue;
-		private System.Windows.Forms.CheckBox chkPerTarget;
-		private System.Windows.Forms.CheckBox chkPersistent;
-		private System.Windows.Forms.Label lblMarker;
-		private System.Windows.Forms.TextBox txtMarker;
+		private Desktop.Skinning.SkinnedGroupBox groupBox1;
+		private Desktop.Skinning.SkinnedRadioButton radClear;
+		private Desktop.Skinning.SkinnedRadioButton radDecrement;
+		private Desktop.Skinning.SkinnedRadioButton radIncrement;
+		private Desktop.Skinning.SkinnedRadioButton radSet;
+		private Desktop.Skinning.SkinnedTextBox txtValue;
+		private Desktop.Skinning.SkinnedCheckBox chkPerTarget;
+		private Desktop.Skinning.SkinnedCheckBox chkPersistent;
+		private Desktop.Skinning.SkinnedLabel lblMarker;
+		private Desktop.Skinning.SkinnedTextBox txtMarker;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/MarkerOptions.cs b/editor source/SPNATI Character Editor/Controls/MarkerOptions.cs
index 43d83e055ae3946b16351e0d6c75675a58fd9952..be7260bf921d1f944d503ba4f672f86b441e85d7 100644
--- a/editor source/SPNATI Character Editor/Controls/MarkerOptions.cs	
+++ b/editor source/SPNATI Character Editor/Controls/MarkerOptions.cs	
@@ -1,4 +1,5 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Controls
@@ -6,6 +7,12 @@ namespace SPNATI_Character_Editor.Controls
 	public partial class MarkerOptions : UserControl, IDialogueDropDownControl
 	{
 		public int RowIndex { get; private set; }
+
+		public SkinnedBackgroundType PanelType
+		{
+			get { return SkinnedBackgroundType.Background; }
+		}
+
 		private DialogueLine _line;
 
 		public event EventHandler DataUpdated;
@@ -13,10 +20,22 @@ namespace SPNATI_Character_Editor.Controls
 		public MarkerOptions()
 		{
 			InitializeComponent();
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			BackColor = skin.Background.Normal;
+			foreach (Control child in Controls)
+			{
+				SkinManager.Instance.ReskinControl(child, skin);
+			}
+			Invalidate(true);
 		}
 
-		public void SetData(int rowIndex, DialogueLine line)
+		public void SetData(int rowIndex, DialogueLine line, Character character)
 		{
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
 			RowIndex = rowIndex;
 			_line = line;
 			string markerValue;
diff --git a/editor source/SPNATI Character Editor/Controls/PartTransparencySlider.Designer.cs b/editor source/SPNATI Character Editor/Controls/PartTransparencySlider.Designer.cs
index 88b509db8a3440935c2822d10d928be2107b7b2c..7cda8bbfe900d8d8dfe1dc67d9a6073174b0ed68 100644
--- a/editor source/SPNATI Character Editor/Controls/PartTransparencySlider.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/PartTransparencySlider.Designer.cs	
@@ -28,9 +28,9 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.lblName = new System.Windows.Forms.Label();
-			this.track = new System.Windows.Forms.TrackBar();
-			this.valValue = new System.Windows.Forms.NumericUpDown();
+			this.lblName = new Desktop.Skinning.SkinnedLabel();
+			this.track = new Desktop.Skinning.SkinnedSlider();
+			this.valValue = new Desktop.Skinning.SkinnedNumericUpDown();
 			((System.ComponentModel.ISupportInitialize)(this.track)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.valValue)).BeginInit();
 			this.SuspendLayout();
@@ -38,7 +38,11 @@
 			// lblName
 			// 
 			this.lblName.AutoSize = true;
-			this.lblName.Location = new System.Drawing.Point(3, 6);
+			this.lblName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblName.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblName.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblName.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblName.Location = new System.Drawing.Point(3, 7);
 			this.lblName.Name = "lblName";
 			this.lblName.Size = new System.Drawing.Size(58, 13);
 			this.lblName.TabIndex = 0;
@@ -48,18 +52,23 @@
 			// 
 			this.track.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.track.LargeChange = 10;
+			this.track.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.track.Increment = 10;
 			this.track.Location = new System.Drawing.Point(110, -1);
 			this.track.Maximum = 100;
+			this.track.Minimum = 0;
 			this.track.Name = "track";
-			this.track.Size = new System.Drawing.Size(144, 45);
-			this.track.SmallChange = 5;
+			this.track.Size = new System.Drawing.Size(144, 31);
 			this.track.TabIndex = 1;
-			this.track.TickFrequency = 10;
+			this.track.TickFrequency = 25;
+			this.track.Value = 0;
 			// 
 			// valValue
 			// 
 			this.valValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.valValue.BackColor = System.Drawing.Color.White;
+			this.valValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valValue.ForeColor = System.Drawing.Color.Black;
 			this.valValue.Location = new System.Drawing.Point(260, 4);
 			this.valValue.Name = "valValue";
 			this.valValue.Size = new System.Drawing.Size(47, 20);
@@ -83,8 +92,8 @@
 
 		#endregion
 
-		private System.Windows.Forms.Label lblName;
-		private System.Windows.Forms.TrackBar track;
-		private System.Windows.Forms.NumericUpDown valValue;
+		private Desktop.Skinning.SkinnedLabel lblName;
+		private Desktop.Skinning.SkinnedSlider track;
+		private Desktop.Skinning.SkinnedNumericUpDown valValue;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/RecordSelectBox.Designer.cs b/editor source/SPNATI Character Editor/Controls/RecordSelectBox.Designer.cs
index 16d9970404cf477f2ab5214b8f911f9e3d456f7b..06cb2917e4c8e230d311d7c67e260ec34d139b63 100644
--- a/editor source/SPNATI Character Editor/Controls/RecordSelectBox.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/RecordSelectBox.Designer.cs	
@@ -28,9 +28,9 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.lstSelectedItems = new System.Windows.Forms.ListBox();
-			this.cmdAdd = new System.Windows.Forms.Button();
-			this.cmdRemove = new System.Windows.Forms.Button();
+			this.lstSelectedItems = new Desktop.Skinning.SkinnedListBox();
+			this.cmdAdd = new Desktop.Skinning.SkinnedButton();
+			this.cmdRemove = new Desktop.Skinning.SkinnedButton();
 			this.recField = new Desktop.CommonControls.RecordField();
 			this.SuspendLayout();
 			// 
@@ -39,11 +39,14 @@
 			this.lstSelectedItems.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstSelectedItems.BackColor = System.Drawing.Color.White;
+			this.lstSelectedItems.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstSelectedItems.ForeColor = System.Drawing.Color.Black;
 			this.lstSelectedItems.FormattingEnabled = true;
 			this.lstSelectedItems.IntegralHeight = false;
 			this.lstSelectedItems.Location = new System.Drawing.Point(0, 27);
 			this.lstSelectedItems.Name = "lstSelectedItems";
-			this.lstSelectedItems.Size = new System.Drawing.Size(223, 92);
+			this.lstSelectedItems.Size = new System.Drawing.Size(205, 92);
 			this.lstSelectedItems.Sorted = true;
 			this.lstSelectedItems.TabIndex = 0;
 			this.lstSelectedItems.SelectedIndexChanged += new System.EventHandler(this.lstSelectedItems_SelectedIndexChanged);
@@ -51,10 +54,13 @@
 			// cmdAdd
 			// 
 			this.cmdAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdAdd.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdAdd.Enabled = false;
-			this.cmdAdd.Location = new System.Drawing.Point(229, 0);
+			this.cmdAdd.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdAdd.Flat = false;
+			this.cmdAdd.Location = new System.Drawing.Point(211, 0);
 			this.cmdAdd.Name = "cmdAdd";
-			this.cmdAdd.Size = new System.Drawing.Size(63, 23);
+			this.cmdAdd.Size = new System.Drawing.Size(79, 23);
 			this.cmdAdd.TabIndex = 2;
 			this.cmdAdd.Text = "Add";
 			this.cmdAdd.UseVisualStyleBackColor = true;
@@ -63,10 +69,13 @@
 			// cmdRemove
 			// 
 			this.cmdRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdRemove.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdRemove.Enabled = false;
-			this.cmdRemove.Location = new System.Drawing.Point(229, 27);
+			this.cmdRemove.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdRemove.Flat = false;
+			this.cmdRemove.Location = new System.Drawing.Point(211, 27);
 			this.cmdRemove.Name = "cmdRemove";
-			this.cmdRemove.Size = new System.Drawing.Size(63, 23);
+			this.cmdRemove.Size = new System.Drawing.Size(79, 23);
 			this.cmdRemove.TabIndex = 3;
 			this.cmdRemove.Text = "Remove";
 			this.cmdRemove.UseVisualStyleBackColor = true;
@@ -82,9 +91,10 @@
 			this.recField.PlaceholderText = null;
 			this.recField.Record = null;
 			this.recField.RecordContext = null;
+			this.recField.RecordFilter = null;
 			this.recField.RecordKey = null;
 			this.recField.RecordType = null;
-			this.recField.Size = new System.Drawing.Size(223, 20);
+			this.recField.Size = new System.Drawing.Size(205, 20);
 			this.recField.TabIndex = 4;
 			this.recField.UseAutoComplete = false;
 			this.recField.RecordChanged += new System.EventHandler<Desktop.CommonControls.RecordEventArgs>(this.recField_RecordChanged);
@@ -105,9 +115,9 @@
 
 		#endregion
 
-		private System.Windows.Forms.ListBox lstSelectedItems;
-		private System.Windows.Forms.Button cmdAdd;
-		private System.Windows.Forms.Button cmdRemove;
+		private Desktop.Skinning.SkinnedListBox lstSelectedItems;
+		private Desktop.Skinning.SkinnedButton cmdAdd;
+		private Desktop.Skinning.SkinnedButton cmdRemove;
 		private Desktop.CommonControls.RecordField recField;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/Reference/TagGuide.Designer.cs b/editor source/SPNATI Character Editor/Controls/Reference/TagGuide.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ba167336d1fcb9dd2884b2de5f732925cd28dc2e
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/Reference/TagGuide.Designer.cs	
@@ -0,0 +1,94 @@
+namespace SPNATI_Character_Editor.Controls.Reference
+{
+	partial class TagGuide
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.label1 = new System.Windows.Forms.Label();
+			this.recTag = new Desktop.CommonControls.RecordField();
+			this.lstItems = new Desktop.CommonControls.AccordionListView();
+			this.SuspendLayout();
+			// 
+			// label1
+			// 
+			this.label1.AutoSize = true;
+			this.label1.Location = new System.Drawing.Point(3, 7);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(44, 13);
+			this.label1.TabIndex = 1;
+			this.label1.Text = "Search:";
+			// 
+			// recTag
+			// 
+			this.recTag.AllowCreate = false;
+			this.recTag.Location = new System.Drawing.Point(53, 3);
+			this.recTag.Name = "recTag";
+			this.recTag.PlaceholderText = null;
+			this.recTag.Record = null;
+			this.recTag.RecordContext = null;
+			this.recTag.RecordFilter = null;
+			this.recTag.RecordKey = null;
+			this.recTag.RecordType = null;
+			this.recTag.Size = new System.Drawing.Size(150, 20);
+			this.recTag.TabIndex = 2;
+			this.recTag.UseAutoComplete = true;
+			this.recTag.RecordChanged += new System.EventHandler<Desktop.CommonControls.RecordEventArgs>(this.recTag_RecordChanged);
+			// 
+			// lstItems
+			// 
+			this.lstItems.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstItems.DataSource = null;
+			this.lstItems.Location = new System.Drawing.Point(0, 26);
+			this.lstItems.Name = "lstItems";
+			this.lstItems.SelectedItem = null;
+			this.lstItems.Size = new System.Drawing.Size(268, 124);
+			this.lstItems.TabIndex = 0;
+			this.lstItems.Load += new System.EventHandler(this.lstItems_Load);
+			// 
+			// TagGuide
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.recTag);
+			this.Controls.Add(this.label1);
+			this.Controls.Add(this.lstItems);
+			this.Name = "TagGuide";
+			this.Size = new System.Drawing.Size(268, 150);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.CommonControls.AccordionListView lstItems;
+		private System.Windows.Forms.Label label1;
+		private Desktop.CommonControls.RecordField recTag;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/Reference/TagGuide.cs b/editor source/SPNATI Character Editor/Controls/Reference/TagGuide.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7b220d69a824cb00b04c1c9280fe986a25803c53
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/Reference/TagGuide.cs	
@@ -0,0 +1,65 @@
+using Desktop;
+using Desktop.CommonControls;
+using System;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Controls.Reference
+{
+	public partial class TagGuide : UserControl
+	{
+		public TagGuide()
+		{
+			InitializeComponent();
+
+			recTag.RecordType = typeof(Tag);
+			recTag.RecordFilter = FilterTags;
+
+			lstItems.AddColumn(new AccordionColumn("Tag", "Key")
+			{
+				Width = 80
+			});
+			lstItems.AddColumn(new AccordionColumn("Description", "Description")
+			{
+				FillWeight = 1
+			});
+
+			lstItems.RebuildColumns();
+		}
+
+		private bool FilterTags(IRecord tag)
+		{
+			return !string.IsNullOrEmpty(tag.Group);
+		}
+
+		private void lstItems_Load(object sender, EventArgs e)
+		{
+			if (DesignMode) { return; }
+
+			TagList list = new TagList();
+			lstItems.DataSource = list;
+		}
+
+		private void recTag_RecordChanged(object sender, RecordEventArgs e)
+		{
+			Tag tag = recTag.Record as Tag;
+			if (tag != null)
+			{
+				lstItems.SelectedItem = tag;
+			}
+		}
+	}
+
+	public class TagList : GroupedList<Tag>
+	{
+		public TagList()
+		{
+			foreach (Tag tag in TagDatabase.Dictionary.Tags)
+			{
+				if (!string.IsNullOrEmpty(tag.Group))
+				{
+					AddItem(tag);
+				}
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/Reference/TagGuide.resx b/editor source/SPNATI Character Editor/Controls/Reference/TagGuide.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/Reference/TagGuide.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/SceneTree.Designer.cs b/editor source/SPNATI Character Editor/Controls/SceneTree.Designer.cs
index 70e4b6c14b46886282ab64d64a42b00c4d84ad03..b1da6db666f87dcb306703342d4f828755bc002c 100644
--- a/editor source/SPNATI Character Editor/Controls/SceneTree.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/SceneTree.Designer.cs	
@@ -51,9 +51,11 @@
 			this.stopAnimationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
 			this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
 			this.fadeEffectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.jumpToSceneToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
 			this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
 			this.waitForAnimationsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
 			this.waitForInputToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+			this.userPromptToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
 			this.tsAddKeyframe = new System.Windows.Forms.ToolStripButton();
 			this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
 			this.tsUp = new System.Windows.Forms.ToolStripButton();
@@ -67,7 +69,6 @@
 			this.tsLock = new System.Windows.Forms.ToolStripButton();
 			this.openFileDialog = new SPNATI_Character_Editor.Controls.CharacterImageDialog();
 			this.toolStripContainer1.ContentPanel.SuspendLayout();
-			this.toolStripContainer1.TopToolStripPanel.SuspendLayout();
 			this.toolStripContainer1.SuspendLayout();
 			this.toolStrip1.SuspendLayout();
 			this.SuspendLayout();
@@ -77,20 +78,18 @@
 			// 
 			// toolStripContainer1.ContentPanel
 			// 
+			this.toolStripContainer1.ContentPanel.Controls.Add(this.toolStrip1);
 			this.toolStripContainer1.ContentPanel.Controls.Add(this.lblDragger);
 			this.toolStripContainer1.ContentPanel.Controls.Add(this.treeScenes);
-			this.toolStripContainer1.ContentPanel.Size = new System.Drawing.Size(357, 380);
+			this.toolStripContainer1.ContentPanel.Size = new System.Drawing.Size(357, 405);
 			this.toolStripContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.toolStripContainer1.Location = new System.Drawing.Point(0, 0);
+			this.toolStripContainer1.Margin = new System.Windows.Forms.Padding(0);
 			this.toolStripContainer1.Name = "toolStripContainer1";
 			this.toolStripContainer1.Size = new System.Drawing.Size(357, 405);
 			this.toolStripContainer1.TabIndex = 4;
 			this.toolStripContainer1.Text = "toolStripContainer1";
 			// 
-			// toolStripContainer1.TopToolStripPanel
-			// 
-			this.toolStripContainer1.TopToolStripPanel.Controls.Add(this.toolStrip1);
-			// 
 			// lblDragger
 			// 
 			this.lblDragger.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
@@ -105,9 +104,8 @@
 			// treeScenes
 			// 
 			this.treeScenes.AllowDrop = true;
-			this.treeScenes.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.treeScenes.HideSelection = false;
-			this.treeScenes.Location = new System.Drawing.Point(0, 0);
+			this.treeScenes.Location = new System.Drawing.Point(0, 25);
 			this.treeScenes.Name = "treeScenes";
 			this.treeScenes.Size = new System.Drawing.Size(357, 380);
 			this.treeScenes.TabIndex = 2;
@@ -120,7 +118,11 @@
 			// 
 			// toolStrip1
 			// 
+			this.toolStrip1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.toolStrip1.AutoSize = false;
 			this.toolStrip1.Dock = System.Windows.Forms.DockStyle.None;
+			this.toolStrip1.GripMargin = new System.Windows.Forms.Padding(0);
 			this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
 			this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.tsAddScene,
@@ -138,9 +140,9 @@
             this.tsDuplicate,
             this.toolStripSeparator7,
             this.tsLock});
-			this.toolStrip1.Location = new System.Drawing.Point(3, 0);
+			this.toolStrip1.Location = new System.Drawing.Point(0, 0);
 			this.toolStrip1.Name = "toolStrip1";
-			this.toolStrip1.Size = new System.Drawing.Size(306, 25);
+			this.toolStrip1.Size = new System.Drawing.Size(357, 25);
 			this.toolStrip1.TabIndex = 4;
 			this.toolStrip1.Text = "toolStrip1";
 			// 
@@ -196,9 +198,11 @@
             this.stopAnimationToolStripMenuItem,
             this.toolStripSeparator4,
             this.fadeEffectToolStripMenuItem,
+            this.jumpToSceneToolStripMenuItem,
             this.toolStripSeparator3,
             this.waitForAnimationsToolStripMenuItem,
-            this.waitForInputToolStripMenuItem});
+            this.waitForInputToolStripMenuItem,
+            this.userPromptToolStripMenuItem});
 			this.tsAddDirective.Image = global::SPNATI_Character_Editor.Properties.Resources.AddChildNode;
 			this.tsAddDirective.ImageTransparentColor = System.Drawing.Color.Magenta;
 			this.tsAddDirective.Name = "tsAddDirective";
@@ -329,6 +333,17 @@
 			this.fadeEffectToolStripMenuItem.Text = "Fade Effect";
 			this.fadeEffectToolStripMenuItem.Click += new System.EventHandler(this.AddDirectiveToolstripItem_Click);
 			// 
+			// jumpToSceneToolStripMenuItem
+			// 
+			this.jumpToSceneToolStripMenuItem.Name = "jumpToSceneToolStripMenuItem";
+			this.jumpToSceneToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) 
+            | System.Windows.Forms.Keys.D2)));
+			this.jumpToSceneToolStripMenuItem.Size = new System.Drawing.Size(277, 22);
+			this.jumpToSceneToolStripMenuItem.Tag = "jump";
+			this.jumpToSceneToolStripMenuItem.Text = "Jump to Scene";
+			this.jumpToSceneToolStripMenuItem.Visible = false;
+			this.jumpToSceneToolStripMenuItem.Click += new System.EventHandler(this.AddDirectiveToolstripItem_Click);
+			// 
 			// toolStripSeparator3
 			// 
 			this.toolStripSeparator3.Name = "toolStripSeparator3";
@@ -353,6 +368,16 @@
 			this.waitForInputToolStripMenuItem.Text = "Wait For Input";
 			this.waitForInputToolStripMenuItem.Click += new System.EventHandler(this.AddDirectiveToolstripItem_Click);
 			// 
+			// userPromptToolStripMenuItem
+			// 
+			this.userPromptToolStripMenuItem.Name = "userPromptToolStripMenuItem";
+			this.userPromptToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Return)));
+			this.userPromptToolStripMenuItem.Size = new System.Drawing.Size(277, 22);
+			this.userPromptToolStripMenuItem.Tag = "prompt";
+			this.userPromptToolStripMenuItem.Text = "User Prompt";
+			this.userPromptToolStripMenuItem.Visible = false;
+			this.userPromptToolStripMenuItem.Click += new System.EventHandler(this.AddDirectiveToolstripItem_Click);
+			// 
 			// tsAddKeyframe
 			// 
 			this.tsAddKeyframe.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
@@ -360,7 +385,7 @@
 			this.tsAddKeyframe.ImageTransparentColor = System.Drawing.Color.Magenta;
 			this.tsAddKeyframe.Name = "tsAddKeyframe";
 			this.tsAddKeyframe.Size = new System.Drawing.Size(23, 22);
-			this.tsAddKeyframe.Text = "Add Keyframe";
+			this.tsAddKeyframe.Text = "Add Keyframe or Choice";
 			this.tsAddKeyframe.Click += new System.EventHandler(this.TsAddKeyframe_Click);
 			// 
 			// toolStripSeparator1
@@ -453,6 +478,7 @@
 			// openFileDialog
 			// 
 			this.openFileDialog.Filter = "";
+			this.openFileDialog.IncludeOpponents = false;
 			this.openFileDialog.UseAbsolutePaths = false;
 			// 
 			// SceneTree
@@ -464,8 +490,6 @@
 			this.Name = "SceneTree";
 			this.Size = new System.Drawing.Size(357, 405);
 			this.toolStripContainer1.ContentPanel.ResumeLayout(false);
-			this.toolStripContainer1.TopToolStripPanel.ResumeLayout(false);
-			this.toolStripContainer1.TopToolStripPanel.PerformLayout();
 			this.toolStripContainer1.ResumeLayout(false);
 			this.toolStripContainer1.PerformLayout();
 			this.toolStrip1.ResumeLayout(false);
@@ -514,5 +538,7 @@
 		private System.Windows.Forms.ToolStripMenuItem emitParticleToolStripMenuItem;
 		private System.Windows.Forms.ToolStripSeparator toolStripSeparator8;
 		private CharacterImageDialog openFileDialog;
+		private System.Windows.Forms.ToolStripMenuItem jumpToSceneToolStripMenuItem;
+		private System.Windows.Forms.ToolStripMenuItem userPromptToolStripMenuItem;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/SceneTree.cs b/editor source/SPNATI Character Editor/Controls/SceneTree.cs
index 029817393643c7f40fbd259445d6e0d75d63f6d4..7f629025edb1cce1c3314bc173fd929defafde60 100644
--- a/editor source/SPNATI Character Editor/Controls/SceneTree.cs	
+++ b/editor source/SPNATI Character Editor/Controls/SceneTree.cs	
@@ -86,6 +86,10 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				BuildKeyframeNode(dirNode, kf);
 			}
+			foreach (Choice c in directive.Choices)
+			{
+				BuildChoiceNode(dirNode, c);
+			}
 			return dirNode;
 		}
 
@@ -98,6 +102,15 @@ namespace SPNATI_Character_Editor.Controls
 			return keyNode;
 		}
 
+		private TreeNode BuildChoiceNode(TreeNode dirNode, Choice choice)
+		{
+			TreeNode choiceNode = new TreeNode(choice.ToString());
+			_nodes[choice] = choiceNode;
+			choiceNode.Tag = choice;
+			dirNode.Nodes.Add(choiceNode);
+			return choiceNode;
+		}
+
 		/// <summary>
 		/// Selects a node in the tree
 		/// </summary>
@@ -129,6 +142,7 @@ namespace SPNATI_Character_Editor.Controls
 			Scene scene = node.Tag as Scene;
 			Directive directive = node.Tag as Directive;
 			Keyframe keyframe = node.Tag as Keyframe;
+			Choice choice = node.Tag as Choice;
 
 			if (scene != null)
 			{
@@ -139,13 +153,18 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				EnableMenu(tsAddDirective, true);
 				DirectiveDefinition def = Definitions.Instance.Get<DirectiveDefinition>(directive.DirectiveType);
-				EnableMenu(tsAddKeyframe, def != null && def.IsAnimatable);
+				EnableMenu(tsAddKeyframe, def != null && (def.IsAnimatable || directive.DirectiveType == "prompt"));
 			}
 			else if (keyframe != null)
 			{
 				EnableMenu(tsAddDirective, false);
 				EnableMenu(tsAddKeyframe, true);
 			}
+			else if (choice != null)
+			{
+				EnableMenu(tsAddDirective, false);
+				EnableMenu(tsAddKeyframe, true);
+			}
 
 			AfterSelect?.Invoke(this, new SceneTreeEventArgs(node));
 		}
@@ -204,7 +223,24 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void TsAddKeyframe_Click(object sender, EventArgs e)
 		{
-			AddKeyframe();
+			TreeNode selectedNode = treeScenes.SelectedNode;
+			Directive directive = selectedNode?.Tag as Directive;
+			if (directive == null)
+			{
+				directive = selectedNode.Parent?.Tag as Directive;
+				if (directive == null)
+				{
+					return;
+				}
+			}
+			if (directive.DirectiveType == "prompt")
+			{
+				AddChoice();
+			}
+			else
+			{
+				AddKeyframe();
+			}
 		}
 
 		private void TsAddDirective_ButtonClick(object sender, EventArgs e)
@@ -218,7 +254,7 @@ namespace SPNATI_Character_Editor.Controls
 			string tag = ctl.Tag?.ToString();
 			if (!string.IsNullOrEmpty(tag))
 			{
-				AddDirective(tag);
+				AddDirective(tag, true);
 			}
 		}
 
@@ -231,7 +267,7 @@ namespace SPNATI_Character_Editor.Controls
 		{
 			TreeNode node = treeScenes.SelectedNode;
 			if (node == null) { return; }
-			string nodeType = (node.Tag is Scene ? (((Scene)node.Tag).Transition ? "transition" : "scene") : node.Tag is Directive ? "directive" : "keyframe");
+			string nodeType = (node.Tag is Scene ? (((Scene)node.Tag).Transition ? "transition" : "scene") : node.Tag is Choice ? "choice" : node.Tag is Directive ? "directive" : "keyframe");
 			if (MessageBox.Show($"Are you sure you want to remove this {nodeType}?", $"Remove {nodeType}", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
 			{
 				return;
@@ -285,15 +321,18 @@ namespace SPNATI_Character_Editor.Controls
 			if (dragNode == null) { return; }
 			bool draggingDirective = dragNode.Tag is Directive;
 			bool draggingKeyframe = dragNode.Tag is Keyframe && !draggingDirective;
+			bool draggingChoice = dragNode.Tag is Choice;
 
 			Point targetPoint = treeScenes.PointToClient(new Point(e.X, e.Y));
 			TreeNode targetNode = treeScenes.GetNodeAt(targetPoint);
 			bool targetDirective = targetNode?.Tag is Directive;
 
 
-			if (targetNode == null || (draggingKeyframe && (targetNode == null || targetNode.Parent != dragNode.Parent)))
+			if (targetNode == null ||
+				(draggingKeyframe && (targetNode == null || targetNode.Parent != dragNode.Parent)) ||
+				 (draggingChoice && (targetNode == null || targetNode.Parent != dragNode.Parent)))
 			{
-				//keyframes can only be dragged within their original parent
+				//keyframes and choices can only be dragged within their original parent
 				e.Effect = DragDropEffects.None;
 				lblDragger.Visible = false;
 				return;
@@ -315,7 +354,7 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				//dragging a directive on top of a scene. always insert below
 				Point pt = lblDragger.Location;
-				pt.Y = targetNode.Bounds.Y + targetNode.Bounds.Height;
+				pt.Y = treeScenes.Top + targetNode.Bounds.Y + targetNode.Bounds.Height;
 				lblDragger.Location = pt;
 				lblDragger.Visible = true;
 			}
@@ -325,7 +364,7 @@ namespace SPNATI_Character_Editor.Controls
 				{
 					//hovering on the upper half of a node
 					Point pt = lblDragger.Location;
-					pt.Y = targetNode.Bounds.Y;
+					pt.Y = treeScenes.Top + targetNode.Bounds.Y;
 					lblDragger.Location = pt;
 					lblDragger.Visible = true;
 				}
@@ -333,7 +372,7 @@ namespace SPNATI_Character_Editor.Controls
 				{
 					//hovering on the lower half
 					Point pt = lblDragger.Location;
-					pt.Y = targetNode.Bounds.Y + targetNode.Bounds.Height;
+					pt.Y = treeScenes.Top + targetNode.Bounds.Y + targetNode.Bounds.Height;
 					lblDragger.Location = pt;
 					lblDragger.Visible = true;
 				}
@@ -351,12 +390,14 @@ namespace SPNATI_Character_Editor.Controls
 			if (dragNode == null) { return; }
 			bool draggingDirective = dragNode.Tag is Directive;
 			bool draggingKeyframe = dragNode.Tag is Keyframe && !draggingDirective;
+			bool draggingChoice = dragNode.Tag is Choice;
 
 			Point targetPoint = treeScenes.PointToClient(new Point(e.X, e.Y));
 			TreeNode targetNode = treeScenes.GetNodeAt(targetPoint);
 			if (targetNode == null) { return; }
 			bool targetDirective = targetNode.Tag is Directive;
 			bool targetKeyframe = targetNode.Tag is Keyframe && !targetDirective;
+			bool targetChoice = targetNode.Tag is Choice;
 
 			if (!dragNode.Equals(targetNode))
 			{
@@ -414,6 +455,21 @@ namespace SPNATI_Character_Editor.Controls
 						}
 					}
 				}
+				else if (draggingChoice)
+				{
+					//dragging a choice
+					if (targetChoice)
+					{
+						if (targetPoint.Y - targetNode.Bounds.Height / 2 < targetNode.Bounds.Y - 2)
+						{
+							MoveChoiceNode(dragNode, targetNode.Index);
+						}
+						else
+						{
+							MoveChoiceNode(dragNode, targetNode.Index + 1);
+						}
+					}
+				}
 				else
 				{
 					//dragging a scene
@@ -528,7 +584,39 @@ namespace SPNATI_Character_Editor.Controls
 			directive.Keyframes.Insert(index, frame);
 			dirNode.Nodes.Insert(index, node);
 
-			//auto-selectit
+			//auto-select it
+			treeScenes.SelectedNode = node;
+		}
+
+		/// <summary>
+		/// Moves a choice to a new position under its directive
+		/// </summary>
+		/// <param name="node"></param>
+		/// <param name="index"></param>
+		private void MoveChoiceNode(TreeNode node, int index)
+		{
+			TreeNode dirNode = node.Parent;
+			if (node.Index < index)
+			{
+				index--;
+			}
+
+			InsertChoice(node, index, dirNode);
+		}
+
+		private void InsertChoice(TreeNode node, int index, TreeNode dirNode)
+		{
+			//remove the node from its old position (which might be nowhere)
+			RemoveNode(node);
+			_nodes[node.Tag] = node;
+
+			Directive directive = dirNode.Tag as Directive;
+			Choice choice = node.Tag as Choice;
+			choice.Directive = directive;
+			directive.Choices.Insert(index, choice);
+			dirNode.Nodes.Insert(index, node);
+
+			//auto-select it
 			treeScenes.SelectedNode = node;
 		}
 
@@ -595,7 +683,7 @@ namespace SPNATI_Character_Editor.Controls
 			treeScenes.SelectedNode = node;
 		}
 
-		private void AddDirective(string type)
+		private void AddDirective(string type, bool autoSelect)
 		{
 			Scene scene = GetSelectedScene();
 			if (scene == null) { return; }
@@ -635,7 +723,15 @@ namespace SPNATI_Character_Editor.Controls
 					selected.Parent.Nodes.Insert(selected.Index + 1, node);
 				}
 
-				treeScenes.SelectedNode = node;
+				if (autoSelect)
+				{
+					treeScenes.SelectedNode = node;
+				}
+			}
+
+			if (Config.AutoPauseDirectives.Contains(type))
+			{
+				AddDirective("pause", false);
 			}
 		}
 
@@ -737,6 +833,37 @@ namespace SPNATI_Character_Editor.Controls
 			treeScenes.SelectedNode = node;
 		}
 
+		private void AddChoice()
+		{
+			TreeNode selectedNode = treeScenes.SelectedNode;
+			if (selectedNode == null || selectedNode.Tag is Scene) { return; }
+
+			Choice choice = new Choice();
+			choice.Caption = "Button Caption";
+			TreeNode node = new TreeNode(choice.ToString());
+			node.Tag = choice;
+			_nodes[choice] = node;
+
+			Directive directive = selectedNode.Tag as Directive;
+			if (directive != null)
+			{
+				//adding to a directive, so insert it at the bottom
+				node.Text = choice.ToString();
+				selectedNode.Nodes.Add(node);
+				directive.Choices.Add(choice);
+			}
+			else
+			{
+				//adding to a choice, so add next to it
+				node.Text = choice.ToString();
+				directive = selectedNode.Parent.Tag as Directive;
+				selectedNode.Parent.Nodes.Insert(selectedNode.Index + 1, node);
+				directive.Choices.Insert(selectedNode.Index + 1, choice);
+			}
+			choice.Directive = directive;
+
+			treeScenes.SelectedNode = node;
+		}
 
 		/// <summary>
 		/// Deletes a node
@@ -772,6 +899,15 @@ namespace SPNATI_Character_Editor.Controls
 				node.Parent.Nodes.Remove(node);
 				_nodes.Remove(kf);
 			}
+
+			Choice choice = node.Tag as Choice;
+			if (choice != null)
+			{
+				dir = node.Parent.Tag as Directive;
+				dir.Choices.Remove(choice);
+				node.Parent.Nodes.Remove(node);
+				_nodes.Remove(choice);
+			}
 		}
 
 		/// <summary>
@@ -806,6 +942,12 @@ namespace SPNATI_Character_Editor.Controls
 				MoveKeyframeNode(node, node.Index - 1);
 				return;
 			}
+
+			Choice choice = node.Tag as Choice;
+			if (choice != null)
+			{
+				MoveChoiceNode(node, node.Index - 1);
+			}
 		}
 
 		/// <summary>
@@ -840,6 +982,13 @@ namespace SPNATI_Character_Editor.Controls
 				MoveKeyframeNode(node, node.Index + 2);
 				return;
 			}
+
+			Choice choice = node.Tag as Choice;
+			if (choice != null)
+			{
+				MoveChoiceNode(node, node.Index + 2);
+				return;
+			}
 		}
 
 		/// <summary>
@@ -885,13 +1034,16 @@ namespace SPNATI_Character_Editor.Controls
 			Scene pastedScene = obj as Scene;
 			Directive pastedDirective = obj as Directive;
 			Keyframe pastedFrame = obj as Keyframe;
+			Choice pastedChoice = obj as Choice;
 
 			Directive selectedDirective = node.Tag as Directive;
 			Keyframe selectedFrame = node.Tag as Keyframe;
+			Choice selectedChoice = node.Tag as Choice;
 
 			TreeNode sceneNode = null;
 			TreeNode dirNode = null;
 			TreeNode frameNode = null;
+			TreeNode choiceNode = null;
 
 			if (selectedDirective != null)
 			{
@@ -905,6 +1057,13 @@ namespace SPNATI_Character_Editor.Controls
 				dirNode = node.Parent;
 				frameNode = node;
 			}
+			else if (selectedChoice != null)
+			{
+				selectedDirective = node.Parent.Tag as Directive;
+				sceneNode = node.Parent.Parent;
+				dirNode = node.Parent;
+				choiceNode = node;
+			}
 			else
 			{
 				sceneNode = node;
@@ -924,6 +1083,10 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				newNode = BuildKeyframeNode(dirNode, pastedFrame);
 			}
+			else if (pastedChoice != null && dirNode != null)
+			{
+				newNode = BuildChoiceNode(dirNode, pastedChoice);
+			}
 
 			//insert the node into the correct location
 			if (pastedScene != null)
@@ -957,6 +1120,20 @@ namespace SPNATI_Character_Editor.Controls
 					}
 				}
 			}
+			else if (pastedChoice != null)
+			{
+				if (dirNode != null)
+				{
+					if (choiceNode != null)
+					{
+						InsertChoice(newNode, choiceNode.Index + 1, dirNode);
+					}
+					else
+					{
+						InsertChoice(newNode, dirNode.Nodes.Count - 1, dirNode);
+					}
+				}
+			}
 		}
 
 		private void tsDuplicate_Click(object sender, EventArgs e)
@@ -998,6 +1175,7 @@ namespace SPNATI_Character_Editor.Controls
 		public Scene Scene;
 		public Directive Directive;
 		public Keyframe Keyframe;
+		public Choice Choice;
 
 		public SceneTreeEventArgs(TreeNode node)
 		{
@@ -1012,6 +1190,7 @@ namespace SPNATI_Character_Editor.Controls
 				if (Directive == null)
 				{
 					Keyframe = node.Tag as Keyframe;
+					Choice = node.Tag as Choice;
 					Directive = node.Parent.Tag as Directive;
 					Scene = node.Parent.Parent.Tag as Scene;
 				}
diff --git a/editor source/SPNATI Character Editor/Controls/SelectBox.Designer.cs b/editor source/SPNATI Character Editor/Controls/SelectBox.Designer.cs
index 813041157a26342fa13677582d95267dee98b6b3..3291c6c0944ce214bfc0b776f08e4f04a2d01175 100644
--- a/editor source/SPNATI Character Editor/Controls/SelectBox.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/SelectBox.Designer.cs	
@@ -28,10 +28,10 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.lstSelectedItems = new System.Windows.Forms.ListBox();
-			this.cboSelectableItems = new System.Windows.Forms.ComboBox();
-			this.cmdAdd = new System.Windows.Forms.Button();
-			this.cmdRemove = new System.Windows.Forms.Button();
+			this.lstSelectedItems = new Desktop.Skinning.SkinnedListBox();
+			this.cboSelectableItems = new Desktop.Skinning.SkinnedComboBox();
+			this.cmdAdd = new Desktop.Skinning.SkinnedButton();
+			this.cmdRemove = new Desktop.Skinning.SkinnedButton();
 			this.SuspendLayout();
 			// 
 			// lstSelectedItems
@@ -39,11 +39,14 @@
 			this.lstSelectedItems.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstSelectedItems.BackColor = System.Drawing.Color.White;
+			this.lstSelectedItems.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstSelectedItems.ForeColor = System.Drawing.Color.Black;
 			this.lstSelectedItems.FormattingEnabled = true;
 			this.lstSelectedItems.IntegralHeight = false;
 			this.lstSelectedItems.Location = new System.Drawing.Point(0, 27);
 			this.lstSelectedItems.Name = "lstSelectedItems";
-			this.lstSelectedItems.Size = new System.Drawing.Size(223, 92);
+			this.lstSelectedItems.Size = new System.Drawing.Size(207, 92);
 			this.lstSelectedItems.Sorted = true;
 			this.lstSelectedItems.TabIndex = 0;
 			this.lstSelectedItems.SelectedIndexChanged += new System.EventHandler(this.lstSelectedItems_SelectedIndexChanged);
@@ -53,24 +56,31 @@
 			this.cboSelectableItems.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.cboSelectableItems.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
-			this.cboSelectableItems.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems;
+			this.cboSelectableItems.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
+			this.cboSelectableItems.DropDownStyle = System.Windows.Forms.ComboBoxStyle.Simple;
+			this.cboSelectableItems.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
 			this.cboSelectableItems.FormattingEnabled = true;
 			this.cboSelectableItems.Location = new System.Drawing.Point(0, 0);
 			this.cboSelectableItems.Name = "cboSelectableItems";
-			this.cboSelectableItems.Size = new System.Drawing.Size(223, 21);
+			this.cboSelectableItems.SelectedIndex = -1;
+			this.cboSelectableItems.SelectedItem = null;
+			this.cboSelectableItems.Size = new System.Drawing.Size(207, 21);
 			this.cboSelectableItems.Sorted = true;
 			this.cboSelectableItems.TabIndex = 1;
 			this.cboSelectableItems.SelectedIndexChanged += new System.EventHandler(this.cboSelectableItems_SelectedIndexChanged);
-			this.cboSelectableItems.TextUpdate += new System.EventHandler(this.cboSelectableItems_TextUpdate);
+			this.cboSelectableItems.TextChanged += new System.EventHandler(this.cboSelectableItems_TextChanged);
 			this.cboSelectableItems.KeyDown += new System.Windows.Forms.KeyEventHandler(this.cboSelectableItems_KeyDown);
 			// 
 			// cmdAdd
 			// 
 			this.cmdAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdAdd.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdAdd.Enabled = false;
-			this.cmdAdd.Location = new System.Drawing.Point(229, 0);
+			this.cmdAdd.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdAdd.Flat = false;
+			this.cmdAdd.Location = new System.Drawing.Point(213, 0);
 			this.cmdAdd.Name = "cmdAdd";
-			this.cmdAdd.Size = new System.Drawing.Size(63, 23);
+			this.cmdAdd.Size = new System.Drawing.Size(77, 23);
 			this.cmdAdd.TabIndex = 2;
 			this.cmdAdd.Text = "Add";
 			this.cmdAdd.UseVisualStyleBackColor = true;
@@ -79,10 +89,13 @@
 			// cmdRemove
 			// 
 			this.cmdRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdRemove.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdRemove.Enabled = false;
-			this.cmdRemove.Location = new System.Drawing.Point(229, 27);
+			this.cmdRemove.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdRemove.Flat = false;
+			this.cmdRemove.Location = new System.Drawing.Point(213, 27);
 			this.cmdRemove.Name = "cmdRemove";
-			this.cmdRemove.Size = new System.Drawing.Size(63, 23);
+			this.cmdRemove.Size = new System.Drawing.Size(77, 23);
 			this.cmdRemove.TabIndex = 3;
 			this.cmdRemove.Text = "Remove";
 			this.cmdRemove.UseVisualStyleBackColor = true;
@@ -104,9 +117,9 @@
 
 		#endregion
 
-		private System.Windows.Forms.ListBox lstSelectedItems;
-		private System.Windows.Forms.ComboBox cboSelectableItems;
-		private System.Windows.Forms.Button cmdAdd;
-		private System.Windows.Forms.Button cmdRemove;
+		private Desktop.Skinning.SkinnedListBox lstSelectedItems;
+		private Desktop.Skinning.SkinnedComboBox cboSelectableItems;
+		private Desktop.Skinning.SkinnedButton cmdAdd;
+		private Desktop.Skinning.SkinnedButton cmdRemove;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/SelectBox.cs b/editor source/SPNATI Character Editor/Controls/SelectBox.cs
index 3bbbad71365727596c8a2007b4a24918011c7a95..1f3871e670bcd42be2234cfe3d6b29aceb8390d9 100644
--- a/editor source/SPNATI Character Editor/Controls/SelectBox.cs	
+++ b/editor source/SPNATI Character Editor/Controls/SelectBox.cs	
@@ -59,12 +59,12 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void cboSelectableItems_SelectedIndexChanged(object sender, EventArgs e)
 		{
-			cmdAdd.Enabled = cboSelectableItems.SelectedIndex >= 0 || !String.IsNullOrWhiteSpace(cboSelectableItems.Text);
+			cmdAdd.Enabled = cboSelectableItems.SelectedIndex >= 0 || !string.IsNullOrWhiteSpace(cboSelectableItems.Text);
 		}
 
-		private void cboSelectableItems_TextUpdate(object sender, EventArgs e)
+		private void cboSelectableItems_TextChanged(object sender, EventArgs e)
 		{
-			cmdAdd.Enabled = cboSelectableItems.SelectedIndex >= 0 || !String.IsNullOrWhiteSpace(cboSelectableItems.Text);
+			cmdAdd.Enabled = cboSelectableItems.SelectedIndex >= 0 || !string.IsNullOrWhiteSpace(cboSelectableItems.Text);
 		}
 
 		private void cmdAdd_Click(object sender, EventArgs e)
diff --git a/editor source/SPNATI Character Editor/Controls/SlicerControls/IntervalSlicerControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/SlicerControls/IntervalSlicerControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f41a7cb5cc6ebf6aa224cc14833793067e01c0a8
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/SlicerControls/IntervalSlicerControl.Designer.cs	
@@ -0,0 +1,85 @@
+namespace SPNATI_Character_Editor.Controls.SlicerControls
+{
+	partial class IntervalSlicerControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.components = new System.ComponentModel.Container();
+			this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
+			this.mnuMerge = new System.Windows.Forms.ContextMenuStrip(this.components);
+			this.sliderSplits = new SPNATI_Character_Editor.Controls.IntervalSlider();
+			((System.ComponentModel.ISupportInitialize)(this.sliderSplits)).BeginInit();
+			this.SuspendLayout();
+			// 
+			// flowPanel
+			// 
+			this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.flowPanel.Location = new System.Drawing.Point(3, 30);
+			this.flowPanel.Name = "flowPanel";
+			this.flowPanel.Size = new System.Drawing.Size(257, 355);
+			this.flowPanel.TabIndex = 4;
+			// 
+			// mnuMerge
+			// 
+			this.mnuMerge.Name = "mnuMerge";
+			this.mnuMerge.Size = new System.Drawing.Size(61, 4);
+			this.mnuMerge.Tag = "Surface";
+			// 
+			// sliderSplits
+			// 
+			this.sliderSplits.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.sliderSplits.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.sliderSplits.Location = new System.Drawing.Point(3, 3);
+			this.sliderSplits.Maximum = 10;
+			this.sliderSplits.Minimum = 0;
+			this.sliderSplits.Name = "sliderSplits";
+			this.sliderSplits.Size = new System.Drawing.Size(257, 21);
+			this.sliderSplits.TabIndex = 6;
+			// 
+			// IntervalSlicerControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.sliderSplits);
+			this.Controls.Add(this.flowPanel);
+			this.Name = "IntervalSlicerControl";
+			this.Size = new System.Drawing.Size(263, 385);
+			((System.ComponentModel.ISupportInitialize)(this.sliderSplits)).EndInit();
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private System.Windows.Forms.FlowLayoutPanel flowPanel;
+		private System.Windows.Forms.ContextMenuStrip mnuMerge;
+		private IntervalSlider sliderSplits;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/SlicerControls/IntervalSlicerControl.cs b/editor source/SPNATI Character Editor/Controls/SlicerControls/IntervalSlicerControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a5ae745a9417f0f658e54b89c074c99f8d43a7aa
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/SlicerControls/IntervalSlicerControl.cs	
@@ -0,0 +1,145 @@
+using Desktop.Reporting;
+using Desktop.Reporting.Controls;
+using SPNATI_Character_Editor.DataSlicers;
+using System;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Controls.SlicerControls
+{
+	[DataSlicerControl(typeof(IntervalSlicer))]
+	public partial class IntervalSlicerControl : UserControl, ISlicerControl
+	{
+		private IntervalSlicer _slicer;
+
+		public IntervalSlicerControl()
+		{
+			InitializeComponent();
+		}
+
+		public void SetSlicer(IDataSlicer slicer)
+		{
+			_slicer = slicer as IntervalSlicer;
+			sliderSplits.Minimum = _slicer.Min;
+			sliderSplits.Maximum = _slicer.Max;
+			CreateGroupControls(true);
+			if (sliderSplits.Splits.Count > 0)
+			{
+				int smallestSplit = sliderSplits.Splits.Min();
+				sliderSplits.Splits.Remove(smallestSplit);
+			}
+			sliderSplits.Splits.CollectionChanged += Splits_CollectionChanged;
+		}
+
+		private void CreateGroupControls(bool createSplits)
+		{
+			flowPanel.Controls.Clear();
+			foreach (ISlicerGroup group in _slicer.Groups)
+			{
+				if (group.Key != "-" && createSplits)
+				{
+					sliderSplits.Splits.Add(group.Values.Max(v => ((Range)v).Min));
+				}
+				AddEntry(group);
+			}
+		}
+
+		private void Splits_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+		{
+			RebuildGroups();
+		}
+
+		private void RebuildGroups()
+		{
+			sliderSplits.Splits.CollectionChanged -= Splits_CollectionChanged;
+			ObservableCollection<ISlicerGroup> groups = new ObservableCollection<ISlicerGroup>();
+			groups.Add(_slicer.NullGroup);
+			int last = sliderSplits.Minimum;
+			for (int i = 0; i < sliderSplits.Splits.Count; i++)
+			{
+				SlicerGroup<Range> group = new SlicerGroup<Range>(new Range(last, sliderSplits.Splits[i] - 1));
+				groups.Add(group);
+				last = sliderSplits.Splits[i];
+				if (i == sliderSplits.Splits.Count - 1)
+				{
+					group = new SlicerGroup<Range>(new Range(last, sliderSplits.Maximum));
+					groups.Add(group);
+				}
+			}
+			_slicer.Groups = groups;
+			CreateGroupControls(false);
+			sliderSplits.Splits.CollectionChanged += Splits_CollectionChanged;
+		}
+
+		private void AddEntry(ISlicerGroup group)
+		{
+			SlicerGroupEntry entry = new SlicerGroupEntry();
+			entry.Removable = false;
+			group.Groupable = false;
+			entry.SetGroup(group);
+			entry.Anchor = AnchorStyles.Left | AnchorStyles.Right;
+			entry.Width = flowPanel.Width - flowPanel.Padding.Left - flowPanel.Padding.Right;
+			flowPanel.Controls.Add(entry);
+		}
+
+		private void Entry_Delete(object sender, EventArgs e)
+		{
+			SlicerGroupEntry entry = sender as SlicerGroupEntry;
+			entry.Group.Active = false;
+			_slicer.RemoveGroup(entry.Group);
+		}
+
+		private void DeleteEntry(SlicerGroupEntry entry)
+		{
+			entry.Delete -= Entry_Delete;
+			entry.Merge -= Entry_Merge;
+			flowPanel.Controls.Remove(entry);
+			_slicer.RemoveGroup(entry.Group);
+		}
+
+		private void Entry_Merge(object sender, EventArgs e)
+		{
+			mnuMerge.Items.Clear();
+			SlicerGroupEntry entry = sender as SlicerGroupEntry;
+			if (entry.Group.Values.Count > 1)
+			{
+				ToolStripItem item = mnuMerge.Items.Add("Split apart");
+				item.Tag = entry;
+				item.Click += Item_Split;
+			}
+			else
+			{
+				foreach (ISlicerGroup group in _slicer.Groups)
+				{
+					if (entry.Group != group && group.Groupable)
+					{
+						ToolStripItem item = mnuMerge.Items.Add($"Merge into {group.Label}");
+						item.Tag = new Tuple<SlicerGroupEntry, ISlicerGroup>(entry, group);
+						item.Click += Item_Click;
+					}
+				}
+			}
+			mnuMerge.Show(MousePosition);
+		}
+
+		private void Item_Split(object sender, EventArgs e)
+		{
+			ToolStripItem item = sender as ToolStripItem;
+			SlicerGroupEntry entry = item.Tag as SlicerGroupEntry;
+			ISlicerGroup group = entry.Group;
+			_slicer.Split(group);
+		}
+
+		private void Item_Click(object sender, EventArgs e)
+		{
+			ToolStripItem item = sender as ToolStripItem;
+			Tuple<SlicerGroupEntry, ISlicerGroup> data = item.Tag as Tuple<SlicerGroupEntry, ISlicerGroup>;
+			SlicerGroupEntry sourceEntry = data.Item1;
+			ISlicerGroup sourceGroup = sourceEntry.Group;
+			ISlicerGroup destGroup = data.Item2;
+			_slicer.Merge(sourceGroup, destGroup);
+			DeleteEntry(sourceEntry);
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/SlicerControls/IntervalSlicerControl.resx b/editor source/SPNATI Character Editor/Controls/SlicerControls/IntervalSlicerControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..ecb79505bd8051bed0db1bb94c3e8b6540d554f1
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/SlicerControls/IntervalSlicerControl.resx	
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="mnuMerge.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/SlicerControls/OneShotSlicerControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/SlicerControls/OneShotSlicerControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..867b0bbe5aba779ca1bfcce6b0405763226c9181
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/SlicerControls/OneShotSlicerControl.Designer.cs	
@@ -0,0 +1,76 @@
+namespace SPNATI_Character_Editor.Controls.SlicerControls
+{
+	partial class OneShotSlicerControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.chkNo = new Desktop.Skinning.SkinnedCheckBox();
+			this.chkYes = new Desktop.Skinning.SkinnedCheckBox();
+			this.SuspendLayout();
+			// 
+			// chkNo
+			// 
+			this.chkNo.AutoSize = true;
+			this.chkNo.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkNo.Location = new System.Drawing.Point(3, 26);
+			this.chkNo.Name = "chkNo";
+			this.chkNo.Size = new System.Drawing.Size(40, 17);
+			this.chkNo.TabIndex = 3;
+			this.chkNo.Text = "No";
+			this.chkNo.UseVisualStyleBackColor = true;
+			this.chkNo.CheckedChanged += new System.EventHandler(this.chkNo_CheckedChanged);
+			// 
+			// chkYes
+			// 
+			this.chkYes.AutoSize = true;
+			this.chkYes.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkYes.Location = new System.Drawing.Point(3, 3);
+			this.chkYes.Name = "chkYes";
+			this.chkYes.Size = new System.Drawing.Size(44, 17);
+			this.chkYes.TabIndex = 2;
+			this.chkYes.Text = "Yes";
+			this.chkYes.UseVisualStyleBackColor = true;
+			this.chkYes.CheckedChanged += new System.EventHandler(this.chkYes_CheckedChanged);
+			// 
+			// OneShotSlicerControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.chkNo);
+			this.Controls.Add(this.chkYes);
+			this.Name = "OneShotSlicerControl";
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedCheckBox chkNo;
+		private Desktop.Skinning.SkinnedCheckBox chkYes;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/SlicerControls/OneShotSlicerControl.cs b/editor source/SPNATI Character Editor/Controls/SlicerControls/OneShotSlicerControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c39dbec0e74b1444f6fffed25767073bdd13ffde
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/SlicerControls/OneShotSlicerControl.cs	
@@ -0,0 +1,38 @@
+using Desktop.Reporting;
+using SPNATI_Character_Editor.DataSlicers;
+using System;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Controls.SlicerControls
+{
+	[DataSlicerControl(typeof(OneShotSlicer))]
+	public partial class OneShotSlicerControl : UserControl, ISlicerControl
+	{
+		private OneShotSlicer _slicer;
+
+		public OneShotSlicerControl()
+		{
+			InitializeComponent();
+		}
+
+		public void SetSlicer(IDataSlicer slicer)
+		{
+			_slicer = slicer as OneShotSlicer;
+			chkYes.Checked = _slicer.YesGroup.Active;
+			chkNo.Checked = _slicer.NoGroup.Active;
+			
+		}
+
+		private void chkYes_CheckedChanged(object sender, EventArgs e)
+		{
+			ISlicerGroup group = _slicer.YesGroup;
+			group.Active = chkYes.Checked;
+		}
+
+		private void chkNo_CheckedChanged(object sender, EventArgs e)
+		{
+			ISlicerGroup group = _slicer.NoGroup;
+			group.Active = chkNo.Checked;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/SlicerControls/OneShotSlicerControl.resx b/editor source/SPNATI Character Editor/Controls/SlicerControls/OneShotSlicerControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/SlicerControls/OneShotSlicerControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/SlicerControls/StageSlicerControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/SlicerControls/StageSlicerControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f5e1068de3961f6a17c25a66153cf2af0c922534
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/SlicerControls/StageSlicerControl.Designer.cs	
@@ -0,0 +1,45 @@
+namespace SPNATI_Character_Editor.Controls.SlicerControls
+{
+	partial class StageSlicerControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.SuspendLayout();
+			// 
+			// StageSlicerControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Name = "StageSlicerControl";
+			this.Size = new System.Drawing.Size(331, 150);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/SlicerControls/StageSlicerControl.cs b/editor source/SPNATI Character Editor/Controls/SlicerControls/StageSlicerControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..61abcc2e1ac80eec77f985e97b088fbcb06b21e4
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/SlicerControls/StageSlicerControl.cs	
@@ -0,0 +1,15 @@
+using Desktop.Reporting;
+using Desktop.Reporting.Controls;
+using SPNATI_Character_Editor.DataSlicers;
+
+namespace SPNATI_Character_Editor.Controls.SlicerControls
+{
+	[DataSlicerControl(typeof(StageSlicer))]
+	public partial class StageSlicerControl : ComboSlicerControl
+	{
+		public StageSlicerControl()
+		{
+			InitializeComponent();
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/SlicerControls/StageSlicerControl.resx b/editor source/SPNATI Character Editor/Controls/SlicerControls/StageSlicerControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/SlicerControls/StageSlicerControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/StageGrid.Designer.cs b/editor source/SPNATI Character Editor/Controls/StageGrid.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e6282bd8865b0145e9bcc03670199111633a3d1f
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StageGrid.Designer.cs	
@@ -0,0 +1,100 @@
+namespace SPNATI_Character_Editor.Controls
+{
+	partial class StageGrid
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.panel = new Desktop.CommonControls.SelectablePanel();
+			this.lblTitle = new Desktop.Skinning.SkinnedLabel();
+			this.chkSelectAll = new Desktop.Skinning.SkinnedCheckBox();
+			this.panel.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// panel
+			// 
+			this.panel.Controls.Add(this.lblTitle);
+			this.panel.Controls.Add(this.chkSelectAll);
+			this.panel.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.panel.Location = new System.Drawing.Point(0, 0);
+			this.panel.Name = "panel";
+			this.panel.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.panel.Size = new System.Drawing.Size(307, 232);
+			this.panel.TabIndex = 3;
+			this.panel.TabSide = Desktop.Skinning.TabSide.None;
+			this.panel.TabStop = true;
+			this.panel.Paint += new System.Windows.Forms.PaintEventHandler(this.panel_Paint);
+			this.panel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.panel_MouseDown);
+			this.panel.MouseLeave += new System.EventHandler(this.panel_MouseLeave);
+			this.panel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.panel_MouseMove);
+			// 
+			// lblTitle
+			// 
+			this.lblTitle.AutoSize = true;
+			this.lblTitle.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.lblTitle.ForeColor = System.Drawing.Color.Blue;
+			this.lblTitle.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblTitle.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.lblTitle.Location = new System.Drawing.Point(-2, -3);
+			this.lblTitle.Margin = new System.Windows.Forms.Padding(0);
+			this.lblTitle.Name = "lblTitle";
+			this.lblTitle.Size = new System.Drawing.Size(55, 21);
+			this.lblTitle.TabIndex = 0;
+			this.lblTitle.Text = "Stages";
+			this.lblTitle.Visible = false;
+			// 
+			// chkSelectAll
+			// 
+			this.chkSelectAll.AutoSize = true;
+			this.chkSelectAll.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkSelectAll.Location = new System.Drawing.Point(0, 19);
+			this.chkSelectAll.Name = "chkSelectAll";
+			this.chkSelectAll.Size = new System.Drawing.Size(15, 14);
+			this.chkSelectAll.TabIndex = 1;
+			this.chkSelectAll.UseVisualStyleBackColor = true;
+			this.chkSelectAll.CheckedChanged += new System.EventHandler(this.chkSelectAll_CheckedChanged);
+			// 
+			// StageGrid
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.AutoSize = true;
+			this.Controls.Add(this.panel);
+			this.Name = "StageGrid";
+			this.Size = new System.Drawing.Size(307, 232);
+			this.panel.ResumeLayout(false);
+			this.panel.PerformLayout();
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private Desktop.CommonControls.SelectablePanel panel;
+		private Desktop.Skinning.SkinnedCheckBox chkSelectAll;
+		private Desktop.Skinning.SkinnedLabel lblTitle;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StageGrid.cs b/editor source/SPNATI Character Editor/Controls/StageGrid.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c7e01a94ade972dd34657be87d3298796976e270
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StageGrid.cs	
@@ -0,0 +1,307 @@
+using Desktop.Skinning;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Controls
+{
+	public partial class StageGrid : UserControl, ISkinControl
+	{
+		private const int CellSize = 30;
+		private const int HeaderPadding = 10;
+
+		private Pen _border = new Pen(Color.DarkGray);
+
+		private Character _character;
+		private int _layerCount;
+		private int _currentStage;
+		private List<bool> _stages = new List<bool>();
+		private Point _highlightedCell = new Point(-1, -1);
+		private Case _case;
+		private bool _populating;
+
+		public event EventHandler CheckedChanged;
+		public event EventHandler<int> LayerSelected;
+
+		private int _headerHeight = 100;
+		public int ColumnHeaderHeight
+		{
+			get { return _headerHeight; }
+			set { _headerHeight = value; ResizeGrid(); }
+		}
+
+		public StageGrid()
+		{
+			InitializeComponent();
+		}
+
+		public bool GetChecked(int index)
+		{
+			return _stages[index];
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			if (Parent != null)
+			{
+				lblTitle.BackColor = Parent.BackColor;
+			}
+			_border = skin.PrimaryColor.GetBorderPen(VisualState.Normal, false, Enabled);
+		}
+
+		private void ResizeGrid()
+		{
+			const int rows = 1;
+			int cols = _layerCount;
+			Width = (int)(cols * CellSize + _headerHeight * 1.67f);
+			Height = rows * CellSize + _headerHeight;
+			RecreateHandle();
+		}
+
+		public void SetPreviewStage(int currentStage)
+		{
+			_currentStage = currentStage;
+			Invalidate();
+		}
+
+		public void SetData(Character character, Case workingCase, int currentStage)
+		{
+			_populating = true;
+			_character = character;
+			_case = workingCase;
+			_currentStage = -1;
+			chkSelectAll.Checked = false;
+			_currentStage = currentStage;
+			_stages.Clear();
+			chkSelectAll.Enabled = (currentStage >= 0);
+			_layerCount = character.Layers + Clothing.ExtraStages;
+			for (int i = 0; i < _layerCount; i++)
+			{
+				_stages.Add(false);
+			}
+			if (workingCase != null)
+			{
+				foreach (int stage in workingCase.Stages)
+				{
+					_stages[stage] = true;
+				}
+			}
+
+			UpdateCheckAllState();
+			ResizeGrid();
+			_populating = false;
+		}
+
+		private void panel_Paint(object sender, PaintEventArgs e)
+		{
+			Graphics g = e.Graphics;
+			g.Clear(Parent.BackColor);
+			StringFormat sf = new StringFormat() { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center, Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.NoWrap };
+
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
+			SolidBrush indicator = skin.SecondaryColor.GetBrush(VisualState.Normal, false, Enabled);
+
+			if (_highlightedCell.X != -1)
+			{
+				using (SolidBrush pointBrush = new SolidBrush(Color.FromArgb(127, skin.SecondaryColor.Normal)))
+				{
+					g.FillRectangle(pointBrush, _highlightedCell.X * CellSize, _headerHeight + _highlightedCell.Y * CellSize, CellSize, CellSize);
+				}
+			}
+
+			using (Brush headerBrush = new SolidBrush(Enabled ? skin.PrimaryForeColor : skin.Surface.DisabledForeColor))
+			{
+				g.DrawString("Stages", Skin.HeaderFont, headerBrush, -2, -3);
+			}
+			using (Brush disabledBrush = new SolidBrush(skin.Surface.DisabledForeColor))
+			{
+				using (Brush fontBrush = new SolidBrush(Enabled ? skin.Surface.ForeColor : skin.Surface.DisabledForeColor))
+				{
+					using (Brush disabledBack = new SolidBrush(skin.Background.Disabled))
+					{
+						g.DrawString("Select All", Skin.TextFont, fontBrush, chkSelectAll.Right + 2, chkSelectAll.Top);
+
+						g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
+
+						Image check = Properties.Resources.Checkmark;
+						Image disabledCheck = Properties.Resources.CheckmarkDisabled;
+						for (int layer = 0; layer < _layerCount; layer++)
+						{
+							bool enabled = IsStageEnabled(layer);
+							if (!enabled)
+							{
+								g.FillRectangle(disabledBack, layer * CellSize, _headerHeight, CellSize, CellSize);
+							}
+
+							if (layer == _currentStage)
+							{
+								g.FillRectangle(indicator, layer * CellSize, _headerHeight + CellSize - 5, CellSize, 5);
+							}
+
+							bool stageChecked = _stages[layer];
+							if (stageChecked)
+							{
+								g.DrawImage(IsStageEnabled(layer) ? check : disabledCheck, layer * CellSize + CellSize / 2 - check.Width / 2, _headerHeight + CellSize / 2 - check.Height / 2, check.Width, check.Height);
+							}
+						}
+
+						g.DrawLine(_border, 0, _headerHeight, 0, Height);
+						g.DrawLine(_border, _headerHeight * 1.67f, 0, Width, 0);
+						g.DrawLine(_border, 0, Height - 1, Width - _headerHeight * 1.67f, Height - 1);
+
+						//Cells
+						for (int i = 0; i < _layerCount; i++)
+						{
+							int x = CellSize * i;
+							g.DrawLine(_border, x, _headerHeight, x, Height);
+							g.DrawLine(_border, x, _headerHeight, x + _headerHeight * 1.67f, 0);
+						}
+						g.DrawLine(_border, Width - _headerHeight * 1.67f, _headerHeight, Width - _headerHeight * 1.67f, Height);
+						g.DrawLine(_border, CellSize * _layerCount, _headerHeight, CellSize * _layerCount + _headerHeight * 1.67f, 0);
+						g.DrawLine(_border, 0, _headerHeight, CellSize * _layerCount, _headerHeight);
+
+						//Column labels
+						for (int i = 0; i < _layerCount; i++)
+						{
+							string label = GetLayerName(i);
+
+							int x = CellSize * i;
+							g.TranslateTransform(x + 13, _headerHeight - 16); //fudging some numbers empirically to make it line up nice
+							g.RotateTransform(-30);
+
+							int width = (int)(Math.Sqrt((_headerHeight * _headerHeight) + (_headerHeight * 1.67f) * (_headerHeight * 1.67f)));
+							g.DrawString(label, Font, IsStageEnabled(i) ? fontBrush : disabledBrush, new Rectangle(0, 0, width, CellSize), sf);
+
+							g.ResetTransform();
+						}
+					}
+				}
+			}
+
+			g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
+		}
+
+		private string GetLayerName(int layer)
+		{
+			StageName stage = _character.LayerToStageName(layer);
+			return stage.DisplayName;
+		}
+
+		private void panel_MouseMove(object sender, MouseEventArgs e)
+		{
+			int x = e.X / CellSize;
+			int y = (e.Y - _headerHeight) / CellSize;
+			Point oldPt = _highlightedCell;
+			if (x >= 0 && x < _layerCount && y == 0 && IsStageEnabled(x))
+			{
+				_highlightedCell.X = x;
+				_highlightedCell.Y = y;
+
+				Cursor = Cursors.Hand;
+			}
+			else
+			{
+				_highlightedCell.X = -1;
+				_highlightedCell.Y = -1;
+				Cursor = Cursors.Default;
+			}
+			if (_highlightedCell.X != oldPt.X || _highlightedCell.Y != oldPt.Y)
+			{
+				panel.Invalidate();
+			}
+		}
+
+		private void panel_MouseLeave(object sender, System.EventArgs e)
+		{
+			if (_highlightedCell.X >= 0)
+			{
+				_highlightedCell.X = -1;
+				_highlightedCell.Y = -1;
+				Cursor = Cursors.Default;
+				panel.Invalidate();
+			}
+		}
+
+		private void panel_MouseDown(object sender, MouseEventArgs e)
+		{
+			int x = e.X / CellSize;
+			int y = (e.Y - _headerHeight) / CellSize;
+			if (x >= 0 && x < _layerCount && y == 0)
+			{
+				if (!IsStageEnabled(x)) { return; }
+				if (e.Button == MouseButtons.Left)
+				{
+					_stages[x] = !_stages[x];
+					_populating = true;
+					CheckedChanged?.Invoke(this, EventArgs.Empty);
+					UpdateCheckAllState();
+					_populating = false;
+				}
+				else if (e.Button == MouseButtons.Right && _stages[x])
+				{
+					LayerSelected?.Invoke(this, x);
+				}
+				panel.Invalidate();
+			}
+		}
+
+		/// <summary>
+		/// Updates the Select All checkbox based on the individual stage checkboxes
+		/// </summary>
+		private void UpdateCheckAllState()
+		{
+			bool allChecked = true;
+			bool noneChecked = true;
+			for (int i = 0; i < _stages.Count; i++)
+			{
+				if (_currentStage != i && IsStageEnabled(i))
+				{
+					if (_stages[i])
+					{
+						noneChecked = false;
+					}
+					else
+					{
+						allChecked = false;
+					}
+				}
+			}
+			if (chkSelectAll.Enabled)
+			{
+				chkSelectAll.CheckState = allChecked ? CheckState.Checked : noneChecked ? CheckState.Unchecked : CheckState.Indeterminate;
+			}
+			else
+			{
+				chkSelectAll.Checked = false;
+			}
+		}
+
+		private void chkSelectAll_CheckedChanged(object sender, EventArgs e)
+		{
+			if (_currentStage == -1 || _populating)
+			{
+				return;
+			}
+			bool newState = chkSelectAll.Checked;
+			for (int i = 0; i < _stages.Count; i++)
+			{
+				if (i == _currentStage)
+					continue;
+				if (IsStageEnabled(i))
+				{
+					_stages[i] = newState;
+				}
+			}
+			Invalidate(true);
+			CheckedChanged?.Invoke(this, e);
+		}
+
+		private bool IsStageEnabled(int i)
+		{
+			if (_case == null) { return false; }
+			return TriggerDatabase.UsedInStage(_case.Tag, _character, i);
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StageGrid.resx b/editor source/SPNATI Character Editor/Controls/StageGrid.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StageGrid.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/StageSpecificGrid.Designer.cs b/editor source/SPNATI Character Editor/Controls/StageSpecificGrid.Designer.cs
index 457c5a63b92285a15b732359891cb7faf3f34618..67289207e9f63a95504080724707d88d6553596a 100644
--- a/editor source/SPNATI Character Editor/Controls/StageSpecificGrid.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/StageSpecificGrid.Designer.cs	
@@ -28,7 +28,7 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.grid = new System.Windows.Forms.DataGridView();
+			this.grid = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColValue = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColStage = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			((System.ComponentModel.ISupportInitialize)(this.grid)).BeginInit();
@@ -74,7 +74,7 @@
 
 		#endregion
 
-		private System.Windows.Forms.DataGridView grid;
+		private Desktop.Skinning.SkinnedDataGridView grid;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColValue;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColStage;
 	}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/StyleControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d6d73a033432b968e3f22f9834f7869284044951
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControl.Designer.cs	
@@ -0,0 +1,367 @@
+namespace SPNATI_Character_Editor.Controls
+{
+	partial class StyleControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.components = new System.ComponentModel.Container();
+			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+			this.toolStrip1 = new System.Windows.Forms.ToolStrip();
+			this.tsAdd = new System.Windows.Forms.ToolStripButton();
+			this.tsRemove = new System.Windows.Forms.ToolStripButton();
+			this.lstStyles = new Desktop.Skinning.SkinnedListBox();
+			this.splitContainer2 = new System.Windows.Forms.SplitContainer();
+			this.txtSample = new Desktop.Skinning.SkinnedTextBox();
+			this.cmdAddAttribute = new Desktop.Skinning.SkinnedButton();
+			this.tableAttributes = new Desktop.CommonControls.PropertyTable();
+			this.lblUsage = new Desktop.Skinning.SkinnedLabel();
+			this.txtName = new Desktop.Skinning.SkinnedTextBox();
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.wbPreview = new System.Windows.Forms.WebBrowser();
+			this.txtAdvanced = new Desktop.Skinning.SkinnedTextBox();
+			this.tmrCreate = new System.Windows.Forms.Timer(this.components);
+			this.txtDescription = new Desktop.Skinning.SkinnedTextBox();
+			this.skinnedLabel2 = new Desktop.Skinning.SkinnedLabel();
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+			this.splitContainer1.Panel1.SuspendLayout();
+			this.splitContainer1.Panel2.SuspendLayout();
+			this.splitContainer1.SuspendLayout();
+			this.toolStrip1.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit();
+			this.splitContainer2.Panel1.SuspendLayout();
+			this.splitContainer2.Panel2.SuspendLayout();
+			this.splitContainer2.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// splitContainer1
+			// 
+			this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.splitContainer1.Location = new System.Drawing.Point(0, 0);
+			this.splitContainer1.Name = "splitContainer1";
+			// 
+			// splitContainer1.Panel1
+			// 
+			this.splitContainer1.Panel1.Controls.Add(this.toolStrip1);
+			this.splitContainer1.Panel1.Controls.Add(this.lstStyles);
+			// 
+			// splitContainer1.Panel2
+			// 
+			this.splitContainer1.Panel2.Controls.Add(this.splitContainer2);
+			this.splitContainer1.Size = new System.Drawing.Size(482, 477);
+			this.splitContainer1.SplitterDistance = 116;
+			this.splitContainer1.TabIndex = 1;
+			// 
+			// toolStrip1
+			// 
+			this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
+			this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.tsAdd,
+            this.tsRemove});
+			this.toolStrip1.Location = new System.Drawing.Point(0, 0);
+			this.toolStrip1.Name = "toolStrip1";
+			this.toolStrip1.Size = new System.Drawing.Size(116, 25);
+			this.toolStrip1.TabIndex = 1;
+			this.toolStrip1.Tag = "Surface";
+			this.toolStrip1.Text = "toolStrip1";
+			// 
+			// tsAdd
+			// 
+			this.tsAdd.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsAdd.Image = global::SPNATI_Character_Editor.Properties.Resources.Add;
+			this.tsAdd.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsAdd.Name = "tsAdd";
+			this.tsAdd.Size = new System.Drawing.Size(23, 22);
+			this.tsAdd.Text = "Add Style";
+			this.tsAdd.Click += new System.EventHandler(this.tsAdd_Click);
+			// 
+			// tsRemove
+			// 
+			this.tsRemove.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsRemove.Image = global::SPNATI_Character_Editor.Properties.Resources.Remove;
+			this.tsRemove.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsRemove.Name = "tsRemove";
+			this.tsRemove.Size = new System.Drawing.Size(23, 22);
+			this.tsRemove.Text = "Remove Style";
+			this.tsRemove.Click += new System.EventHandler(this.tsRemove_Click);
+			// 
+			// lstStyles
+			// 
+			this.lstStyles.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstStyles.BackColor = System.Drawing.Color.White;
+			this.lstStyles.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstStyles.ForeColor = System.Drawing.Color.Black;
+			this.lstStyles.FormattingEnabled = true;
+			this.lstStyles.Location = new System.Drawing.Point(3, 27);
+			this.lstStyles.Name = "lstStyles";
+			this.lstStyles.Size = new System.Drawing.Size(113, 433);
+			this.lstStyles.TabIndex = 0;
+			this.lstStyles.SelectedIndexChanged += new System.EventHandler(this.lstStyles_SelectedIndexChanged);
+			// 
+			// splitContainer2
+			// 
+			this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.splitContainer2.Location = new System.Drawing.Point(0, 0);
+			this.splitContainer2.Name = "splitContainer2";
+			this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal;
+			// 
+			// splitContainer2.Panel1
+			// 
+			this.splitContainer2.Panel1.Controls.Add(this.txtDescription);
+			this.splitContainer2.Panel1.Controls.Add(this.skinnedLabel2);
+			this.splitContainer2.Panel1.Controls.Add(this.txtSample);
+			this.splitContainer2.Panel1.Controls.Add(this.cmdAddAttribute);
+			this.splitContainer2.Panel1.Controls.Add(this.tableAttributes);
+			this.splitContainer2.Panel1.Controls.Add(this.lblUsage);
+			this.splitContainer2.Panel1.Controls.Add(this.txtName);
+			this.splitContainer2.Panel1.Controls.Add(this.skinnedLabel1);
+			// 
+			// splitContainer2.Panel2
+			// 
+			this.splitContainer2.Panel2.Controls.Add(this.skinnedPanel1);
+			this.splitContainer2.Size = new System.Drawing.Size(362, 477);
+			this.splitContainer2.SplitterDistance = 342;
+			this.splitContainer2.TabIndex = 0;
+			// 
+			// txtSample
+			// 
+			this.txtSample.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtSample.BackColor = System.Drawing.Color.White;
+			this.txtSample.ForeColor = System.Drawing.Color.Black;
+			this.txtSample.Location = new System.Drawing.Point(92, 318);
+			this.txtSample.Name = "txtSample";
+			this.txtSample.ReadOnly = true;
+			this.txtSample.Size = new System.Drawing.Size(267, 20);
+			this.txtSample.TabIndex = 5;
+			// 
+			// cmdAddAttribute
+			// 
+			this.cmdAddAttribute.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdAddAttribute.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdAddAttribute.Flat = true;
+			this.cmdAddAttribute.ForeColor = System.Drawing.Color.Blue;
+			this.cmdAddAttribute.Image = global::SPNATI_Character_Editor.Properties.Resources.Add;
+			this.cmdAddAttribute.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			this.cmdAddAttribute.Location = new System.Drawing.Point(3, 56);
+			this.cmdAddAttribute.Name = "cmdAddAttribute";
+			this.cmdAddAttribute.Size = new System.Drawing.Size(56, 23);
+			this.cmdAddAttribute.TabIndex = 3;
+			this.cmdAddAttribute.Text = "Add";
+			this.cmdAddAttribute.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+			this.cmdAddAttribute.UseVisualStyleBackColor = true;
+			this.cmdAddAttribute.Click += new System.EventHandler(this.cmdAddAttribute_Click);
+			// 
+			// tableAttributes
+			// 
+			this.tableAttributes.AllowDelete = true;
+			this.tableAttributes.AllowFavorites = false;
+			this.tableAttributes.AllowHelp = false;
+			this.tableAttributes.AllowMacros = false;
+			this.tableAttributes.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.tableAttributes.BackColor = System.Drawing.Color.White;
+			this.tableAttributes.Data = null;
+			this.tableAttributes.HeaderType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.tableAttributes.HideAddField = true;
+			this.tableAttributes.HideSpeedButtons = false;
+			this.tableAttributes.Location = new System.Drawing.Point(3, 56);
+			this.tableAttributes.ModifyingProperty = null;
+			this.tableAttributes.Name = "tableAttributes";
+			this.tableAttributes.PanelType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.tableAttributes.PlaceholderText = null;
+			this.tableAttributes.PreserveControls = false;
+			this.tableAttributes.PreviewData = null;
+			this.tableAttributes.RemoveCaption = "Remove";
+			this.tableAttributes.RowHeaderWidth = 70F;
+			this.tableAttributes.RunInitialAddEvents = false;
+			this.tableAttributes.Size = new System.Drawing.Size(359, 256);
+			this.tableAttributes.Sorted = false;
+			this.tableAttributes.TabIndex = 4;
+			this.tableAttributes.UndoManager = null;
+			this.tableAttributes.UseAutoComplete = false;
+			// 
+			// lblUsage
+			// 
+			this.lblUsage.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.lblUsage.AutoSize = true;
+			this.lblUsage.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblUsage.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.lblUsage.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblUsage.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.lblUsage.Location = new System.Drawing.Point(2, 321);
+			this.lblUsage.Name = "lblUsage";
+			this.lblUsage.Size = new System.Drawing.Size(84, 13);
+			this.lblUsage.TabIndex = 2;
+			this.lblUsage.Text = "Dialogue usage:";
+			// 
+			// txtName
+			// 
+			this.txtName.BackColor = System.Drawing.Color.White;
+			this.txtName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtName.ForeColor = System.Drawing.Color.Black;
+			this.txtName.Location = new System.Drawing.Point(71, 3);
+			this.txtName.Name = "txtName";
+			this.txtName.Size = new System.Drawing.Size(138, 20);
+			this.txtName.TabIndex = 1;
+			this.txtName.TextChanged += new System.EventHandler(this.txtName_TextChanged);
+			this.txtName.Validating += new System.ComponentModel.CancelEventHandler(this.txtName_Validating);
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.AutoSize = true;
+			this.skinnedLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel1.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel1.Location = new System.Drawing.Point(3, 6);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(62, 13);
+			this.skinnedLabel1.TabIndex = 0;
+			this.skinnedLabel1.Text = "Style name:";
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.wbPreview);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 0);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.Primary;
+			this.skinnedPanel1.Size = new System.Drawing.Size(362, 131);
+			this.skinnedPanel1.TabIndex = 1;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// wbPreview
+			// 
+			this.wbPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.wbPreview.Location = new System.Drawing.Point(3, 3);
+			this.wbPreview.MinimumSize = new System.Drawing.Size(20, 20);
+			this.wbPreview.Name = "wbPreview";
+			this.wbPreview.ScrollBarsEnabled = false;
+			this.wbPreview.Size = new System.Drawing.Size(356, 125);
+			this.wbPreview.TabIndex = 0;
+			this.wbPreview.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.wbPreview_DocumentCompleted);
+			// 
+			// txtAdvanced
+			// 
+			this.txtAdvanced.AcceptsReturn = true;
+			this.txtAdvanced.AcceptsTab = true;
+			this.txtAdvanced.BackColor = System.Drawing.Color.White;
+			this.txtAdvanced.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.txtAdvanced.Font = new System.Drawing.Font("Courier New", 10F);
+			this.txtAdvanced.ForeColor = System.Drawing.Color.Black;
+			this.txtAdvanced.Location = new System.Drawing.Point(0, 0);
+			this.txtAdvanced.Multiline = true;
+			this.txtAdvanced.Name = "txtAdvanced";
+			this.txtAdvanced.Size = new System.Drawing.Size(482, 477);
+			this.txtAdvanced.TabIndex = 2;
+			this.txtAdvanced.TabStop = false;
+			// 
+			// tmrCreate
+			// 
+			this.tmrCreate.Tick += new System.EventHandler(this.tmrCreate_Tick);
+			// 
+			// txtDescription
+			// 
+			this.txtDescription.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtDescription.BackColor = System.Drawing.Color.White;
+			this.txtDescription.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtDescription.ForeColor = System.Drawing.Color.Black;
+			this.txtDescription.Location = new System.Drawing.Point(71, 29);
+			this.txtDescription.Name = "txtDescription";
+			this.txtDescription.Size = new System.Drawing.Size(288, 20);
+			this.txtDescription.TabIndex = 2;
+			this.txtDescription.TextChanged += new System.EventHandler(this.txtDescription_TextChanged);
+			// 
+			// skinnedLabel2
+			// 
+			this.skinnedLabel2.AutoSize = true;
+			this.skinnedLabel2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel2.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.skinnedLabel2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel2.Location = new System.Drawing.Point(3, 32);
+			this.skinnedLabel2.Name = "skinnedLabel2";
+			this.skinnedLabel2.Size = new System.Drawing.Size(63, 13);
+			this.skinnedLabel2.TabIndex = 6;
+			this.skinnedLabel2.Text = "Description:";
+			// 
+			// StyleControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.splitContainer1);
+			this.Controls.Add(this.txtAdvanced);
+			this.Name = "StyleControl";
+			this.Size = new System.Drawing.Size(482, 477);
+			this.splitContainer1.Panel1.ResumeLayout(false);
+			this.splitContainer1.Panel1.PerformLayout();
+			this.splitContainer1.Panel2.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+			this.splitContainer1.ResumeLayout(false);
+			this.toolStrip1.ResumeLayout(false);
+			this.toolStrip1.PerformLayout();
+			this.splitContainer2.Panel1.ResumeLayout(false);
+			this.splitContainer2.Panel1.PerformLayout();
+			this.splitContainer2.Panel2.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit();
+			this.splitContainer2.ResumeLayout(false);
+			this.skinnedPanel1.ResumeLayout(false);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedListBox lstStyles;
+		private System.Windows.Forms.SplitContainer splitContainer1;
+		private System.Windows.Forms.ToolStrip toolStrip1;
+		private System.Windows.Forms.ToolStripButton tsAdd;
+		private System.Windows.Forms.ToolStripButton tsRemove;
+		private System.Windows.Forms.SplitContainer splitContainer2;
+		private System.Windows.Forms.WebBrowser wbPreview;
+		private Desktop.Skinning.SkinnedTextBox txtName;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
+		private Desktop.Skinning.SkinnedLabel lblUsage;
+		private Desktop.Skinning.SkinnedTextBox txtAdvanced;
+		private Desktop.CommonControls.PropertyTable tableAttributes;
+		private Desktop.Skinning.SkinnedButton cmdAddAttribute;
+		private Desktop.Skinning.SkinnedTextBox txtSample;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private System.Windows.Forms.Timer tmrCreate;
+		private Desktop.Skinning.SkinnedTextBox txtDescription;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel2;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControl.cs b/editor source/SPNATI Character Editor/Controls/StyleControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b9e8f23ee2fe4f6d36943dd089a6e20234a75c3a
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControl.cs	
@@ -0,0 +1,209 @@
+using Desktop;
+using SPNATI_Character_Editor.IO;
+using SPNATI_Character_Editor.Providers;
+using System.Collections.Generic;
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Controls
+{
+	public partial class StyleControl : UserControl
+	{
+		private Character _character;
+		private CharacterStyleSheet _sheet;
+		private HtmlElement _previewElement;
+		private StyleRule _activeRule;
+
+		public StyleControl()
+		{
+			InitializeComponent();
+		}
+
+		public void SetCharacter(Character character)
+		{
+			_character = character;
+			CharacterStyleSheet sheet = _character.Styles;
+			_sheet = sheet;
+			if (_sheet != null)
+			{
+				PopulateSheet();
+			}
+			else
+			{
+				splitContainer1.Panel2.Visible = false;
+			}
+		}
+
+		private void PopulateSheet()
+		{
+			splitContainer1.Panel2.Visible = true;
+			txtAdvanced.Text = _sheet.FullText;
+			if (_sheet.AdvancedMode)
+			{
+				splitContainer1.Visible = false;
+			}
+			else
+			{
+				string path = Path.GetFullPath(Path.Combine("Resources", "preview.html"));
+				wbPreview.Navigate(path);
+				_sheet.Rules.CollectionChanged += Rules_CollectionChanged;
+				PopulateStyles();
+			}
+		}
+
+		private void Rules_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+		{
+			switch (e.Action)
+			{
+				case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
+					lstStyles.Items.Add(e.NewItems[0]);
+					lstStyles.SelectedIndex = e.NewStartingIndex;
+					break;
+				case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
+					lstStyles.Items.Remove(e.OldItems[0]);
+					break;
+			}
+		}
+
+		private void PopulateStyles()
+		{
+			foreach (StyleRule rule in _sheet.Rules)
+			{
+				lstStyles.Items.Add(rule);
+			}
+		}
+
+		public void Save()
+		{
+			if (_sheet == null) { return; }
+			_character.StyleSheetName = _sheet.Name;
+			_sheet.FullText = txtAdvanced.Text;
+		}
+
+		private void wbPreview_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
+		{
+			splitContainer1.Panel2.Visible = false;
+			_previewElement = wbPreview.Document.GetElementById("preview");
+		}
+
+		private void tsAdd_Click(object sender, System.EventArgs e)
+		{
+			if (_sheet == null)
+			{
+				_character.StyleSheetName = "styles.css"; //this will force a default stylesheet to be created
+				_sheet = _character.Styles;
+				PopulateSheet();
+				tmrCreate.Start();
+			}
+			else
+			{
+				StyleRule rule = new StyleRule() { ClassName = "style" };
+				_sheet.Rules.Add(rule);
+			}
+		}
+
+		private void tsRemove_Click(object sender, System.EventArgs e)
+		{
+			if (_activeRule != null)
+			{
+				_sheet.Rules.Remove(_activeRule);
+			}
+		}
+
+		private void lstStyles_SelectedIndexChanged(object sender, System.EventArgs e)
+		{
+			if (_activeRule != null)
+			{
+				_activeRule.PropertyChanged -= _activeRule_PropertyChanged;
+			}
+			_activeRule = lstStyles.SelectedItem as StyleRule;
+			UpdatePreview();
+			if (_activeRule != null)
+			{
+				txtName.Text = _activeRule.ClassName;
+				txtDescription.Text = _activeRule.Description;
+				splitContainer1.Panel2.Visible = true;
+				tableAttributes.Data = _activeRule;
+				_activeRule.PropertyChanged += _activeRule_PropertyChanged;
+			}
+			else
+			{
+				splitContainer1.Panel2.Visible = false;
+				tableAttributes.Data = null;
+				txtName.Text = "";
+				txtDescription.Text = "";
+			}
+		}
+
+		private void _activeRule_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			if (e.PropertyName == "ClassName")
+			{
+				lstStyles.RefreshListItems();
+			}
+			UpdatePreview();
+		}
+
+		private void UpdatePreview()
+		{
+			if (_previewElement != null && _activeRule != null)
+			{
+				txtSample.Text = $"This is the {{{_activeRule.ClassName}}}Preview Text{{!reset}} for the style.";
+				List<string> styles = new List<string>();
+				foreach (StyleAttribute attr in _activeRule.Attributes)
+				{
+					styles.Add($"{attr.Name}: {attr.Value}");
+				}
+				_previewElement.Style = string.Join(";", styles);
+			}
+		}
+
+		private void txtDescription_TextChanged(object sender, System.EventArgs e)
+		{
+			if (_activeRule != null)
+			{
+				_activeRule.Description = txtDescription.Text;
+			}
+		}
+
+		private void txtName_TextChanged(object sender, System.EventArgs e)
+		{
+			if (_activeRule != null)
+			{
+				_activeRule.ClassName = txtName.Text;
+			}
+		}
+
+		private void txtName_Validating(object sender, System.ComponentModel.CancelEventArgs e)
+		{
+			string text = txtName.Text;
+			text =  Regex.Replace(text, @"\W", "");
+			txtName.Text = text;
+		}
+
+		private void cmdAddAttribute_Click(object sender, System.EventArgs e)
+		{
+			if (_activeRule == null) { return; }
+
+			StyleAttributeRecord record = RecordLookup.DoLookup(typeof(StyleAttributeRecord), "", false, null, true, null) as StyleAttributeRecord;
+			if (record != null)
+			{
+				StyleAttribute attribute = new StyleAttribute(record.Key, "");
+				if (record.Key == "other")
+				{
+					attribute.Name = "";
+				}
+				_activeRule.Attributes.Add(attribute);
+				tableAttributes.Data = null;
+				tableAttributes.Data = _activeRule;
+			}
+		}
+
+		private void tmrCreate_Tick(object sender, System.EventArgs e)
+		{
+			tmrCreate.Stop();
+			lstStyles.SelectedIndex = 0;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControl.resx b/editor source/SPNATI Character Editor/Controls/StyleControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..f2afcaaaac685d050020b5cff8ec3a49e08b2490
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControl.resx	
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <metadata name="tmrCreate.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>122, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleAttributeEditControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleAttributeEditControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..16813a39c310ff454e7a41f73204b70fcc8147c2
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleAttributeEditControl.Designer.cs	
@@ -0,0 +1,94 @@
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	partial class StyleAttributeEditControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.txtAttribute = new Desktop.CommonControls.TextField();
+			this.skinnedLabel2 = new Desktop.Skinning.SkinnedLabel();
+			this.txtValue = new Desktop.CommonControls.TextField();
+			this.SuspendLayout();
+			// 
+			// txtAttribute
+			// 
+			this.txtAttribute.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.txtAttribute.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.txtAttribute.Location = new System.Drawing.Point(3, 1);
+			this.txtAttribute.Multiline = false;
+			this.txtAttribute.Name = "txtAttribute";
+			this.txtAttribute.PlaceholderText = "";
+			this.txtAttribute.ReadOnly = false;
+			this.txtAttribute.Size = new System.Drawing.Size(91, 20);
+			this.txtAttribute.TabIndex = 1;
+			// 
+			// skinnedLabel2
+			// 
+			this.skinnedLabel2.AutoSize = true;
+			this.skinnedLabel2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel2.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.skinnedLabel2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel2.Location = new System.Drawing.Point(95, 5);
+			this.skinnedLabel2.Name = "skinnedLabel2";
+			this.skinnedLabel2.Size = new System.Drawing.Size(37, 13);
+			this.skinnedLabel2.TabIndex = 2;
+			this.skinnedLabel2.Text = "Value:";
+			// 
+			// txtValue
+			// 
+			this.txtValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtValue.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.txtValue.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.txtValue.Location = new System.Drawing.Point(133, 1);
+			this.txtValue.Multiline = false;
+			this.txtValue.Name = "txtValue";
+			this.txtValue.PlaceholderText = "";
+			this.txtValue.ReadOnly = false;
+			this.txtValue.Size = new System.Drawing.Size(308, 20);
+			this.txtValue.TabIndex = 3;
+			// 
+			// StyleAttributeEditControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.txtValue);
+			this.Controls.Add(this.txtAttribute);
+			this.Controls.Add(this.skinnedLabel2);
+			this.Name = "StyleAttributeEditControl";
+			this.Size = new System.Drawing.Size(446, 23);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+		private Desktop.CommonControls.TextField txtAttribute;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel2;
+		private Desktop.CommonControls.TextField txtValue;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleAttributeEditControl.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleAttributeEditControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5e2ae8a3dbd1101c07b972c68487e208d796a8f1
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleAttributeEditControl.cs	
@@ -0,0 +1,282 @@
+using Desktop;
+using Desktop.CommonControls;
+using SPNATI_Character_Editor.Providers;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	public partial class StyleAttributeEditControl : PropertyEditControl
+	{
+		private SubAttributeControl _subcontrol;
+
+		public StyleAttributeEditControl()
+		{
+			InitializeComponent();
+		}
+
+		protected override void OnIndexChanged()
+		{
+			if (_subcontrol != null)
+			{
+				_subcontrol.Index = Index;
+			}
+		}
+
+		protected override void OnBoundData()
+		{
+			StyleAttribute attribute = GetValue() as StyleAttribute;
+			SwitchMode(attribute);
+			if (_subcontrol != null)
+			{
+				return;
+			}
+			txtAttribute.Text = attribute.Name;
+			txtValue.Text = attribute.Value;
+		}
+
+		public override void OnAddedToRow()
+		{
+			if (_subcontrol != null)
+			{
+				_subcontrol.OnAddedToRow();
+				OnChangeLabel(_subcontrol.Attribute.Name);
+			}
+		}
+
+		protected override void OnDestroy()
+		{
+			if (_subcontrol != null)
+			{
+				_subcontrol.Destroy();
+			}
+			base.OnDestroy();
+		}
+
+		protected override void AddHandlers()
+		{
+			if (_subcontrol != null)
+			{
+				_subcontrol.AddEventHandlers();
+			}
+			else
+			{
+				txtAttribute.TextChanged += Text_ValueChanged;
+				txtValue.TextChanged += Text_ValueChanged;
+			}
+		}
+
+		protected override void RemoveHandlers()
+		{
+			if (_subcontrol != null)
+			{
+				_subcontrol.AddEventHandlers();
+			}
+			else
+			{
+				txtAttribute.TextChanged -= Text_ValueChanged;
+				txtValue.TextChanged -= Text_ValueChanged;
+			}
+		}
+
+		private void DestroySubControl()
+		{
+			if (_subcontrol != null)
+			{
+				Controls.Remove(_subcontrol);
+				_subcontrol.Destroy();
+				foreach (Control ctl in Controls)
+				{
+					ctl.Visible = true;
+				}
+				_subcontrol = null;
+			}
+		}
+
+		/// <summary>
+		/// Switches to a more appropriate control specialized for a particular attribute
+		/// </summary>
+		private void SwitchMode(StyleAttribute attribute)
+		{
+			DestroySubControl();
+
+			string name = attribute.Name;
+			if (!string.IsNullOrEmpty(name))
+			{
+				StyleAttributeRecord record = Definitions.Instance.Get<StyleAttributeRecord>(name);
+				if (record != null && record.ControlType != null)
+				{
+					_subcontrol = Activator.CreateInstance(record.ControlType) as SubAttributeControl;
+					if (_subcontrol != null)
+					{
+						foreach (Control ctl in Controls)
+						{
+							ctl.Visible = false;
+						}
+
+						_subcontrol.ParentControl = this;
+						_subcontrol.Attribute = attribute;
+						_subcontrol.Margin = new Padding(0);
+						_subcontrol.Left = 0;
+						_subcontrol.Top = 0;
+						_subcontrol.Dock = DockStyle.Fill;
+						_subcontrol.SetData(Data, Property, Index, Context, UndoManager, PreviewData, ParentTable);
+						_subcontrol.RemoveEventHandlers(); //these'll be added back when this control's AddHandlers gets called, so this is a method to prevent double handlers
+						Controls.Add(_subcontrol);
+					}
+				}
+			}
+		}
+
+		private void Text_ValueChanged(object sender, EventArgs e)
+		{
+			Save();
+		}
+
+		protected override void OnClear()
+		{
+			if (_subcontrol != null)
+			{
+				_subcontrol.Clear();
+			}
+			else
+			{
+				RemoveHandlers();
+				txtAttribute.Text = "";
+				txtValue.Text = "";
+				AddHandlers();
+				Save();
+			}
+		}
+
+		protected override void OnSave()
+		{
+			if (_subcontrol != null)
+			{
+				_subcontrol.Save();
+			}
+			else
+			{
+				StyleAttribute attribute = GetValue() as StyleAttribute;
+				attribute.Name = txtAttribute.Text;
+				attribute.Value = txtValue.Text;
+			}
+		}
+	}
+
+	public class StyleAttributeEditControlAttribute : EditControlAttribute
+	{
+		public override Type EditControlType
+		{
+			get { return typeof(StyleAttributeEditControl); }
+		}
+	}
+
+	public class SubAttributeControl : PropertyEditControl
+	{
+		public PropertyEditControl ParentControl;
+		public StyleAttribute Attribute;
+
+		public void AddEventHandlers()
+		{
+			AddHandlers();
+		}
+
+		public void RemoveEventHandlers()
+		{
+			RemoveHandlers();
+		}
+
+		public override bool IsUpdating
+		{
+			get
+			{
+				return base.IsUpdating || ParentControl.IsUpdating;
+			}
+		}
+
+		protected StyleAttribute GetAttribute(string name)
+		{
+			IList list = GetList();
+			if (list != null)
+			{
+				foreach (object item in list)
+				{
+					StyleAttribute attrib = item as StyleAttribute;
+					if (attrib != null && attrib.Name == name)
+					{
+						return attrib;
+					}
+				}
+			}
+			return null;
+		}
+
+		public override void BuildMacro(List<string> values)
+		{
+			Save();
+			StyleAttribute attribute = GetValue() as StyleAttribute;
+			values.Add(attribute.Name);
+			values.Add(attribute.Value);
+		}
+
+		protected string[] GetPieces()
+		{
+			string value = Attribute.Value;
+			string[] pieces = value.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
+			return pieces;
+		}
+
+		protected float PxToInt(string px)
+		{
+			if (px.EndsWith("px") || px.EndsWith("pt"))
+			{
+				px = px.Substring(0, px.Length - 2);
+			}
+			float value;
+			float.TryParse(px, out value);
+			return value;
+		}
+
+		protected float ToPts(string value)
+		{
+			float amount = PxToInt(value);
+			if (value.EndsWith("px"))
+			{
+				amount *= 0.75f;
+			}
+			return amount;
+		}
+
+		protected string IntToPx(int value)
+		{
+			if (value == 0)
+			{
+				return "0";
+			}
+			else
+			{
+				return $"{value}px";
+			}
+		}
+	}
+
+	/// <summary>
+	/// Maps a control to a particular CSS attribute.
+	/// </summary>
+	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+	public class SubAttributeAttribute : Attribute
+	{
+		public string Attribute { get; private set; }
+		public string Description { get; private set; }
+
+		public SubAttributeAttribute(string attribute, string description)
+		{
+			Attribute = attribute;
+			Description = description;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleAttributeEditControl.resx b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleAttributeEditControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleAttributeEditControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleColorControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleColorControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b198f7cb42afef45d8baa74e2120c52159ce77f9
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleColorControl.Designer.cs	
@@ -0,0 +1,59 @@
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	partial class StyleColorControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.fldColor = new Desktop.CommonControls.ColorField();
+			this.SuspendLayout();
+			// 
+			// fldColor
+			// 
+			this.fldColor.BackColor = System.Drawing.SystemColors.Control;
+			this.fldColor.Color = System.Drawing.SystemColors.Control;
+			this.fldColor.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.fldColor.Location = new System.Drawing.Point(3, 0);
+			this.fldColor.Name = "fldColor";
+			this.fldColor.Size = new System.Drawing.Size(75, 21);
+			this.fldColor.TabIndex = 1;
+			this.fldColor.UseVisualStyleBackColor = true;
+			// 
+			// StyleColorControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.fldColor);
+			this.Name = "StyleColorControl";
+			this.Size = new System.Drawing.Size(265, 21);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+		private Desktop.CommonControls.ColorField fldColor;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleColorControl.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleColorControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..068bebf8b5b4556a89298f8158573e833245fa59
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleColorControl.cs	
@@ -0,0 +1,42 @@
+using System.Drawing;
+
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	[SubAttribute("color", "Text color")]
+	[SubAttribute("background-color", "Background color")]
+	public partial class StyleColorControl : SubAttributeControl
+	{
+		public StyleColorControl()
+		{
+			InitializeComponent();
+		}
+
+		protected override void OnBoundData()
+		{
+			try
+			{
+				fldColor.Color = ColorTranslator.FromHtml(Attribute.Value);
+			}
+			catch
+			{
+				fldColor.Color = Color.Black;
+			}
+		}
+
+		protected override void AddHandlers()
+		{
+			fldColor.ColorChanged += FldColor_ColorChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			fldColor.ColorChanged -= FldColor_ColorChanged;
+		}
+
+		private void FldColor_ColorChanged(object sender, System.EventArgs e)
+		{
+			string color = ColorTranslator.ToHtml(fldColor.Color);
+			Attribute.Value = color;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleColorControl.resx b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleColorControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleColorControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontFamilyControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontFamilyControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c0de4ff2a85780ca419393ccb7fdec96248423df
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontFamilyControl.Designer.cs	
@@ -0,0 +1,67 @@
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	partial class StyleFontFamilyControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.lblFont = new Desktop.Skinning.SkinnedLinkLabel();
+			this.fontDialog1 = new System.Windows.Forms.FontDialog();
+			this.SuspendLayout();
+			// 
+			// lblFont
+			// 
+			this.lblFont.AutoSize = true;
+			this.lblFont.Location = new System.Drawing.Point(3, 4);
+			this.lblFont.Name = "lblFont";
+			this.lblFont.Size = new System.Drawing.Size(28, 13);
+			this.lblFont.TabIndex = 0;
+			this.lblFont.TabStop = true;
+			this.lblFont.Text = "Font";
+			this.lblFont.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lblFont_LinkClicked);
+			// 
+			// fontDialog1
+			// 
+			this.fontDialog1.ShowEffects = false;
+			// 
+			// StyleFontFamilyControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.lblFont);
+			this.Name = "StyleFontFamilyControl";
+			this.Size = new System.Drawing.Size(150, 21);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedLinkLabel lblFont;
+		private System.Windows.Forms.FontDialog fontDialog1;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontFamilyControl.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontFamilyControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..637c94c08a59540fc4c5b34f04660a710cc73d10
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontFamilyControl.cs	
@@ -0,0 +1,85 @@
+using System.Drawing;
+
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	//[SubAttribute("font-family", "Text font")]
+	public partial class StyleFontFamilyControl : SubAttributeControl
+	{
+		public StyleFontFamilyControl()
+		{
+			InitializeComponent();
+		}
+
+		protected override void OnBoundData()
+		{
+			string font = Attribute.Value;
+			if (string.IsNullOrEmpty(font))
+			{
+				font = "Choose a font";
+			}
+			lblFont.Text = font;
+		}
+
+		private void lblFont_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
+		{
+			float size = 16;
+			FontStyle style = FontStyle.Regular;
+			StyleAttribute sizeAttrib = GetAttribute("font-size");
+			if (sizeAttrib != null)
+			{
+				size = ToPts(sizeAttrib.Value);
+			}
+			StyleAttribute weightAttrib = GetAttribute("font-weight");
+			if (weightAttrib != null)
+			{
+				if (weightAttrib.Value == "700")
+				{
+					style |= FontStyle.Bold;
+				}
+			}
+			StyleAttribute styleAttrib = GetAttribute("font-style");
+			if (styleAttrib != null)
+			{
+				if (styleAttrib.Value == "italic" || styleAttrib.Value == "oblique")
+				{
+					style |= FontStyle.Italic;
+				}
+			}
+
+			fontDialog1.Font = new Font(lblFont.Text, size, style);
+
+			if (fontDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
+			{
+				lblFont.Text = fontDialog1.Font.Name;
+				Attribute.Value = lblFont.Text;
+
+				if (sizeAttrib != null)
+				{
+					sizeAttrib.Value = fontDialog1.Font.SizeInPoints + "pt";
+				}
+				if (weightAttrib != null)
+				{
+					if (fontDialog1.Font.Style.HasFlag(FontStyle.Bold))
+					{
+						weightAttrib.Value = "700";
+					}
+					else
+					{
+						weightAttrib.Value = "400";
+					}
+				}
+				if (styleAttrib != null)
+				{
+					if (fontDialog1.Font.Style.HasFlag(FontStyle.Italic))
+					{
+						styleAttrib.Value = "italic";
+					}
+					else
+					{
+						styleAttrib.Value = "normal";
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontFamilyControl.resx b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontFamilyControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..5db00f709cc4da4046bc3325bdfedd50519ccda4
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontFamilyControl.resx	
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="fontDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontSizeControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontSizeControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3ae55bdd719d06525544b413ae31755258b20e0d
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontSizeControl.Designer.cs	
@@ -0,0 +1,102 @@
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	partial class StyleFontSizeControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.valSize = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.radPt = new Desktop.Skinning.SkinnedRadioButton();
+			this.radPx = new Desktop.Skinning.SkinnedRadioButton();
+			((System.ComponentModel.ISupportInitialize)(this.valSize)).BeginInit();
+			this.SuspendLayout();
+			// 
+			// valSize
+			// 
+			this.valSize.BackColor = System.Drawing.Color.White;
+			this.valSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valSize.ForeColor = System.Drawing.Color.Black;
+			this.valSize.Location = new System.Drawing.Point(3, 1);
+			this.valSize.Minimum = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+			this.valSize.Name = "valSize";
+			this.valSize.Size = new System.Drawing.Size(48, 20);
+			this.valSize.TabIndex = 0;
+			this.valSize.Value = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+			// 
+			// radPt
+			// 
+			this.radPt.AutoSize = true;
+			this.radPt.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.radPt.Location = new System.Drawing.Point(54, 2);
+			this.radPt.Name = "radPt";
+			this.radPt.Size = new System.Drawing.Size(34, 17);
+			this.radPt.TabIndex = 1;
+			this.radPt.TabStop = true;
+			this.radPt.Text = "pt";
+			this.radPt.UseVisualStyleBackColor = true;
+			// 
+			// radPx
+			// 
+			this.radPx.AutoSize = true;
+			this.radPx.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.radPx.Location = new System.Drawing.Point(91, 2);
+			this.radPx.Name = "radPx";
+			this.radPx.Size = new System.Drawing.Size(36, 17);
+			this.radPx.TabIndex = 2;
+			this.radPx.TabStop = true;
+			this.radPx.Text = "px";
+			this.radPx.UseVisualStyleBackColor = true;
+			// 
+			// StyleFontSizeControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.radPx);
+			this.Controls.Add(this.radPt);
+			this.Controls.Add(this.valSize);
+			this.Name = "StyleFontSizeControl";
+			this.Size = new System.Drawing.Size(150, 21);
+			((System.ComponentModel.ISupportInitialize)(this.valSize)).EndInit();
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedNumericUpDown valSize;
+		private Desktop.Skinning.SkinnedRadioButton radPt;
+		private Desktop.Skinning.SkinnedRadioButton radPx;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontSizeControl.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontSizeControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6527a7e540acf32fb33a3cbc508b5b0ba2e4d7f3
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontSizeControl.cs	
@@ -0,0 +1,83 @@
+using System;
+using System.Text.RegularExpressions;
+
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	[SubAttribute("font-size", "Font size")]
+	public partial class StyleFontSizeControl : SubAttributeControl
+	{
+		public StyleFontSizeControl()
+		{
+			InitializeComponent();
+		}
+
+		private bool _bound = false;
+
+		protected override void OnBoundData()
+		{
+			if (!_bound)
+			{
+				_bound = true;
+				Attribute.PropertyChanged += Attribute_PropertyChanged;
+			}
+			string value = Attribute.Value;
+			radPx.Checked = true;
+			if (value.EndsWith("pt"))
+			{
+				radPt.Checked = true;
+			}
+			float num;
+			value = Regex.Replace(value, @"[^0-9\.]*", "");
+			float.TryParse(value, out num);
+			if (num == 0)
+			{
+				num = 16;
+			}
+			valSize.Value = Math.Min(valSize.Maximum, Math.Max(valSize.Minimum, (int)num));
+		}
+
+		protected override void OnDestroy()
+		{
+			Attribute.PropertyChanged -= Attribute_PropertyChanged;
+			base.OnDestroy();
+		}
+
+		private void Attribute_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			if (IsUpdating) { return; }
+			RemoveHandlers();
+			OnBoundData();
+			AddHandlers();
+		}
+
+		protected override void AddHandlers()
+		{
+			valSize.ValueChanged += ValSize_ValueChanged;
+			radPt.CheckedChanged += ValSize_ValueChanged;
+			radPx.CheckedChanged += ValSize_ValueChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			valSize.ValueChanged -= ValSize_ValueChanged;
+			radPt.CheckedChanged -= ValSize_ValueChanged;
+			radPx.CheckedChanged -= ValSize_ValueChanged;
+		}
+
+		private void ValSize_ValueChanged(object sender, EventArgs e)
+		{
+			Save();
+		}
+
+		protected override void OnSave()
+		{
+			int size = (int)valSize.Value;
+			string unit = "px";
+			if (radPt.Checked)
+			{
+				unit = "pt";
+			}
+			Attribute.Value = $"{size}{unit}";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontSizeControl.resx b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontSizeControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontSizeControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontStyleControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontStyleControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..cb98c155c8dd822f9a3758d5953f0e84e469c6ad
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontStyleControl.Designer.cs	
@@ -0,0 +1,77 @@
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	partial class StyleFontStyleControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.radNormal = new Desktop.Skinning.SkinnedRadioButton();
+			this.radItalic = new Desktop.Skinning.SkinnedRadioButton();
+			this.SuspendLayout();
+			// 
+			// radNormal
+			// 
+			this.radNormal.AutoSize = true;
+			this.radNormal.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.radNormal.Location = new System.Drawing.Point(3, 1);
+			this.radNormal.Name = "radNormal";
+			this.radNormal.Size = new System.Drawing.Size(58, 17);
+			this.radNormal.TabIndex = 0;
+			this.radNormal.TabStop = true;
+			this.radNormal.Text = "Normal";
+			this.radNormal.UseVisualStyleBackColor = true;
+			// 
+			// radItalic
+			// 
+			this.radItalic.AutoSize = true;
+			this.radItalic.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.radItalic.Location = new System.Drawing.Point(67, 1);
+			this.radItalic.Name = "radItalic";
+			this.radItalic.Size = new System.Drawing.Size(47, 17);
+			this.radItalic.TabIndex = 1;
+			this.radItalic.TabStop = true;
+			this.radItalic.Text = "Italic";
+			this.radItalic.UseVisualStyleBackColor = true;
+			// 
+			// StyleFontStyleControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.radItalic);
+			this.Controls.Add(this.radNormal);
+			this.Name = "StyleFontStyleControl";
+			this.Size = new System.Drawing.Size(205, 21);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedRadioButton radNormal;
+		private Desktop.Skinning.SkinnedRadioButton radItalic;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontStyleControl.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontStyleControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6f279251e445148cb11148d7646f05780c06a1d0
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontStyleControl.cs	
@@ -0,0 +1,68 @@
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	[SubAttribute("font-style", "Font style (italics)")]
+	public partial class StyleFontStyleControl : SubAttributeControl
+	{
+		public StyleFontStyleControl()
+		{
+			InitializeComponent();
+		}
+
+		private bool _bound;
+
+		protected override void OnBoundData()
+		{
+			if (!_bound)
+			{
+				_bound = true;
+				Attribute.PropertyChanged += Attribute_PropertyChanged;
+			}
+
+			if (Attribute.Value == "italic" || Attribute.Value == "oblique")
+			{
+				//yeah, these aren't actually the same, but who cares
+				radItalic.Checked = true;
+			}
+			else
+			{
+				radNormal.Checked = true;
+			}
+		}
+
+		protected override void RemoveHandlers()
+		{
+			radNormal.CheckedChanged -= RadNormal_CheckedChanged;
+			radItalic.CheckedChanged -= RadNormal_CheckedChanged;
+		}
+		protected override void AddHandlers()
+		{
+			radNormal.CheckedChanged += RadNormal_CheckedChanged;
+			radItalic.CheckedChanged += RadNormal_CheckedChanged;
+		}
+
+		private void RadNormal_CheckedChanged(object sender, System.EventArgs e)
+		{
+			Save();
+		}
+
+		private void Attribute_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			if (IsUpdating) { return; }
+			RemoveHandlers();
+			OnBoundData();
+			AddHandlers();
+		}
+
+		protected override void OnSave()
+		{
+			if (radItalic.Checked)
+			{
+				Attribute.Value = "italic";
+			}
+			else
+			{
+				Attribute.Value = "normal";
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontStyleControl.resx b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontStyleControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontStyleControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontVariantControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontVariantControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bf6a9142806cf822bea6c2da9459256d2b683336
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontVariantControl.Designer.cs	
@@ -0,0 +1,65 @@
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	partial class StyleFontVariantControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.cboVariant = new Desktop.Skinning.SkinnedComboBox();
+			this.SuspendLayout();
+			// 
+			// cboVariant
+			// 
+			this.cboVariant.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboVariant.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboVariant.BackColor = System.Drawing.Color.White;
+			this.cboVariant.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboVariant.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboVariant.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboVariant.Location = new System.Drawing.Point(3, 0);
+			this.cboVariant.Name = "cboVariant";
+			this.cboVariant.SelectedIndex = -1;
+			this.cboVariant.SelectedItem = null;
+			this.cboVariant.Size = new System.Drawing.Size(139, 21);
+			this.cboVariant.Sorted = false;
+			this.cboVariant.TabIndex = 0;
+			// 
+			// StyleFontVariantControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.cboVariant);
+			this.Name = "StyleFontVariantControl";
+			this.Size = new System.Drawing.Size(163, 21);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedComboBox cboVariant;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontVariantControl.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontVariantControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..cc311b4a8dc62abc0706ecbb603c72f1881a9e58
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontVariantControl.cs	
@@ -0,0 +1,48 @@
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	[SubAttribute("font-variant", "Font variant like small caps")]
+	public partial class StyleFontVariantControl : SubAttributeControl
+	{
+		public StyleFontVariantControl()
+		{
+			InitializeComponent();
+			cboVariant.Items.AddRange(new string[] {
+				"normal",
+				"small-caps",
+				});
+		}
+
+		protected override void OnBoundData()
+		{
+			string value = Attribute.Value?.ToLower();
+			if (value == "small-caps")
+			{
+				cboVariant.SelectedItem = "small-caps";
+			}
+			else
+			{
+				cboVariant.SelectedItem = "normal";
+			}
+		}
+
+		protected override void AddHandlers()
+		{
+			cboVariant.SelectedIndexChanged += CboVariant_SelectedIndexChanged;
+		}
+		protected override void RemoveHandlers()
+		{
+			cboVariant.SelectedIndexChanged -= CboVariant_SelectedIndexChanged;
+		}
+
+		private void CboVariant_SelectedIndexChanged(object sender, System.EventArgs e)
+		{
+			Save();
+		}
+
+		protected override void OnSave()
+		{
+			string item = cboVariant.SelectedItem?.ToString() ?? "normal";
+			Attribute.Value = item;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontVariantControl.resx b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontVariantControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontVariantControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontWeightControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontWeightControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8fdc9567333f0feffdbfa4e4aa5718a24a1b52a2
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontWeightControl.Designer.cs	
@@ -0,0 +1,128 @@
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	partial class StyleFontWeightControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.radNormal = new Desktop.Skinning.SkinnedRadioButton();
+			this.radBold = new Desktop.Skinning.SkinnedRadioButton();
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.valWeight = new Desktop.Skinning.SkinnedNumericUpDown();
+			((System.ComponentModel.ISupportInitialize)(this.valWeight)).BeginInit();
+			this.SuspendLayout();
+			// 
+			// radNormal
+			// 
+			this.radNormal.AutoSize = true;
+			this.radNormal.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.radNormal.Location = new System.Drawing.Point(3, 2);
+			this.radNormal.Name = "radNormal";
+			this.radNormal.Size = new System.Drawing.Size(58, 17);
+			this.radNormal.TabIndex = 0;
+			this.radNormal.TabStop = true;
+			this.radNormal.Text = "Normal";
+			this.radNormal.UseVisualStyleBackColor = true;
+			// 
+			// radBold
+			// 
+			this.radBold.AutoSize = true;
+			this.radBold.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.radBold.Location = new System.Drawing.Point(67, 2);
+			this.radBold.Name = "radBold";
+			this.radBold.Size = new System.Drawing.Size(46, 17);
+			this.radBold.TabIndex = 1;
+			this.radBold.TabStop = true;
+			this.radBold.Text = "Bold";
+			this.radBold.UseVisualStyleBackColor = true;
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.AutoSize = true;
+			this.skinnedLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.skinnedLabel1.Location = new System.Drawing.Point(120, 4);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(44, 13);
+			this.skinnedLabel1.TabIndex = 2;
+			this.skinnedLabel1.Text = "Weight:";
+			// 
+			// valWeight
+			// 
+			this.valWeight.BackColor = System.Drawing.Color.White;
+			this.valWeight.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valWeight.ForeColor = System.Drawing.Color.Black;
+			this.valWeight.Increment = new decimal(new int[] {
+            100,
+            0,
+            0,
+            0});
+			this.valWeight.Location = new System.Drawing.Point(170, 1);
+			this.valWeight.Maximum = new decimal(new int[] {
+            900,
+            0,
+            0,
+            0});
+			this.valWeight.Minimum = new decimal(new int[] {
+            100,
+            0,
+            0,
+            0});
+			this.valWeight.Name = "valWeight";
+			this.valWeight.Size = new System.Drawing.Size(57, 20);
+			this.valWeight.TabIndex = 3;
+			this.valWeight.Value = new decimal(new int[] {
+            100,
+            0,
+            0,
+            0});
+			// 
+			// StyleFontWeightControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.valWeight);
+			this.Controls.Add(this.skinnedLabel1);
+			this.Controls.Add(this.radBold);
+			this.Controls.Add(this.radNormal);
+			this.Name = "StyleFontWeightControl";
+			this.Size = new System.Drawing.Size(263, 21);
+			((System.ComponentModel.ISupportInitialize)(this.valWeight)).EndInit();
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedRadioButton radNormal;
+		private Desktop.Skinning.SkinnedRadioButton radBold;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
+		private Desktop.Skinning.SkinnedNumericUpDown valWeight;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontWeightControl.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontWeightControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b11b43aed4e28f478a75d0a20145f847218f9d8b
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontWeightControl.cs	
@@ -0,0 +1,123 @@
+using System;
+
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	[SubAttribute("font-weight", "Font weight")]
+	public partial class StyleFontWeightControl : SubAttributeControl
+	{
+		public StyleFontWeightControl()
+		{
+			InitializeComponent();
+		}
+
+		private bool _bound;
+
+		protected override void OnBoundData()
+		{
+			if (!_bound)
+			{
+				_bound = true;
+				Attribute.PropertyChanged += Attribute_PropertyChanged;
+			}
+			string weight = Attribute.Value;
+			if (string.IsNullOrEmpty(weight))
+			{
+				weight = "400";
+			}
+			weight = weight.ToLower();
+			if (weight == "400" || weight == "normal")
+			{
+				radNormal.Checked = true;
+				valWeight.Value = 400;
+			}
+			else if (weight == "700" || weight == "bold")
+			{
+				radBold.Checked = true;
+				valWeight.Value = 700;
+			}
+			else
+			{
+				int wt;
+				radBold.Checked = false;
+				radNormal.Checked = false;
+				if (int.TryParse(Attribute.Value, out wt))
+				{
+					valWeight.Value = Math.Min(valWeight.Maximum, Math.Max(valWeight.Minimum, wt));
+				}
+				else
+				{
+					valWeight.Value = 400;
+				}
+			}
+		}
+
+		protected override void OnDestroy()
+		{
+			Attribute.PropertyChanged -= Attribute_PropertyChanged;
+			base.OnDestroy();
+		}
+
+		protected override void RemoveHandlers()
+		{
+			radBold.CheckedChanged -= RadBold_CheckedChanged;
+			radNormal.CheckedChanged -= RadBold_CheckedChanged;
+			valWeight.ValueChanged -= ValWeight_ValueChanged;
+		}
+
+		protected override void AddHandlers()
+		{
+			radBold.CheckedChanged += RadBold_CheckedChanged;
+			radNormal.CheckedChanged += RadBold_CheckedChanged;
+			valWeight.ValueChanged += ValWeight_ValueChanged;
+		}
+
+		private void ValWeight_ValueChanged(object sender, EventArgs e)
+		{
+			RemoveHandlers();
+			if (valWeight.Value == 400)
+			{
+				radNormal.Checked = true;
+			}
+			else if (valWeight.Value == 700)
+			{
+				radBold.Checked = true;
+			}
+			else
+			{
+				radBold.Checked = false;
+				radNormal.Checked = false;
+			}
+			Save();
+			AddHandlers();
+		}
+
+		private void RadBold_CheckedChanged(object sender, EventArgs e)
+		{
+			RemoveHandlers();
+			if (radNormal.Checked)
+			{
+				valWeight.Value = 400;
+			}
+			else if (radBold.Checked)
+			{
+				valWeight.Value = 700;
+			}
+			AddHandlers();
+			Save();
+		}
+
+		private void Attribute_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			if (IsUpdating) { return; }
+			RemoveHandlers();
+			OnBoundData();
+			AddHandlers();
+		}
+
+		protected override void OnSave()
+		{
+			int weight = (int)valWeight.Value;
+			Attribute.Value = weight.ToString();
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontWeightControl.resx b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontWeightControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleFontWeightControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextDecorationControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextDecorationControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..599e7480cf7594d9071c449ce1d45a8729c012fd
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextDecorationControl.Designer.cs	
@@ -0,0 +1,125 @@
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	partial class StyleTextDecorationControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.chkUnder = new Desktop.Skinning.SkinnedCheckBox();
+			this.chkOver = new Desktop.Skinning.SkinnedCheckBox();
+			this.chkStrike = new Desktop.Skinning.SkinnedCheckBox();
+			this.cboStyle = new Desktop.Skinning.SkinnedComboBox();
+			this.fldColor = new Desktop.CommonControls.ColorField();
+			this.SuspendLayout();
+			// 
+			// chkUnder
+			// 
+			this.chkUnder.AutoSize = true;
+			this.chkUnder.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkUnder.Location = new System.Drawing.Point(3, 2);
+			this.chkUnder.Name = "chkUnder";
+			this.chkUnder.Size = new System.Drawing.Size(71, 17);
+			this.chkUnder.TabIndex = 0;
+			this.chkUnder.Text = "Underline";
+			this.chkUnder.UseVisualStyleBackColor = true;
+			// 
+			// chkOver
+			// 
+			this.chkOver.AutoSize = true;
+			this.chkOver.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkOver.Location = new System.Drawing.Point(69, 2);
+			this.chkOver.Name = "chkOver";
+			this.chkOver.Size = new System.Drawing.Size(65, 17);
+			this.chkOver.TabIndex = 1;
+			this.chkOver.Text = "Overline";
+			this.chkOver.UseVisualStyleBackColor = true;
+			// 
+			// chkStrike
+			// 
+			this.chkStrike.AutoSize = true;
+			this.chkStrike.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkStrike.Location = new System.Drawing.Point(128, 2);
+			this.chkStrike.Name = "chkStrike";
+			this.chkStrike.Size = new System.Drawing.Size(89, 17);
+			this.chkStrike.TabIndex = 2;
+			this.chkStrike.Text = "Strikethrough";
+			this.chkStrike.UseVisualStyleBackColor = true;
+			// 
+			// cboStyle
+			// 
+			this.cboStyle.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.None;
+			this.cboStyle.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;
+			this.cboStyle.BackColor = System.Drawing.Color.White;
+			this.cboStyle.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.cboStyle.FieldType = Desktop.Skinning.SkinnedFieldType.Surface;
+			this.cboStyle.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.cboStyle.Location = new System.Drawing.Point(213, 0);
+			this.cboStyle.Name = "cboStyle";
+			this.cboStyle.SelectedIndex = -1;
+			this.cboStyle.SelectedItem = null;
+			this.cboStyle.Size = new System.Drawing.Size(79, 21);
+			this.cboStyle.Sorted = false;
+			this.cboStyle.TabIndex = 3;
+			this.cboStyle.Text = "skinnedComboBox1";
+			this.cboStyle.Visible = false;
+			// 
+			// fldColor
+			// 
+			this.fldColor.BackColor = System.Drawing.SystemColors.Control;
+			this.fldColor.Color = System.Drawing.SystemColors.Control;
+			this.fldColor.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.fldColor.Location = new System.Drawing.Point(296, 0);
+			this.fldColor.Name = "fldColor";
+			this.fldColor.Size = new System.Drawing.Size(48, 21);
+			this.fldColor.TabIndex = 4;
+			this.fldColor.UseVisualStyleBackColor = true;
+			this.fldColor.Visible = false;
+			// 
+			// StyleTextDecorationControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.fldColor);
+			this.Controls.Add(this.cboStyle);
+			this.Controls.Add(this.chkStrike);
+			this.Controls.Add(this.chkOver);
+			this.Controls.Add(this.chkUnder);
+			this.Name = "StyleTextDecorationControl";
+			this.Size = new System.Drawing.Size(394, 21);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedCheckBox chkUnder;
+		private Desktop.Skinning.SkinnedCheckBox chkOver;
+		private Desktop.Skinning.SkinnedCheckBox chkStrike;
+		private Desktop.Skinning.SkinnedComboBox cboStyle;
+		private Desktop.CommonControls.ColorField fldColor;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextDecorationControl.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextDecorationControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..85ef9015ae92c123b153c65e3ff3e8842b470f65
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextDecorationControl.cs	
@@ -0,0 +1,123 @@
+using System;
+using System.Drawing;
+
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	[SubAttribute("text-decoration", "Underline, strikethrough, etc.")]
+	public partial class StyleTextDecorationControl : SubAttributeControl
+	{
+		public StyleTextDecorationControl()
+		{
+			InitializeComponent();
+			cboStyle.Items.AddRange(new string[] {
+				"solid",
+				"double",
+				"dotted",
+				"dashed",
+				"wavy",
+			});
+		}
+
+		protected override void OnBoundData()
+		{
+			string value = Attribute.Value?.ToLower()?.Trim() ?? "";
+			int lineCount = 0;
+			if (value.Contains("underline"))
+			{
+				chkUnder.Checked = true;
+				lineCount++;
+			}
+			if (value.Contains("overline"))
+			{
+				chkOver.Checked = true;
+				lineCount++;
+			}
+			if (value.Contains("line-through"))
+			{
+				chkStrike.Checked = true;
+				lineCount++;
+			}
+			string[] pieces = value.Split(' ');
+			int pieceCount = pieces.Length - lineCount;
+			if (pieceCount > 2)
+			{
+				//color style
+				try
+				{
+					Color color = ColorTranslator.FromHtml(pieces[pieces.Length - 2]);
+					fldColor.Color = color;
+				}
+				catch { }
+			}
+			if (pieceCount > 1)
+			{
+				//color|style
+				string piece = pieces[pieces.Length - 1];
+				if (cboStyle.Items.Contains(pieces))
+				{
+					cboStyle.SelectedItem = pieces;
+				}
+				else
+				{
+					try
+					{
+						Color color = ColorTranslator.FromHtml(pieces[pieces.Length - 2]);
+						fldColor.Color = color;
+					}
+					catch { }
+				}
+			}
+			if (pieceCount <= 0)
+			{
+				fldColor.Color = Color.Black;
+			}
+		}
+
+		protected override void RemoveHandlers()
+		{
+			chkOver.CheckedChanged -= ChkOver_CheckedChanged;
+			chkUnder.CheckedChanged -= ChkOver_CheckedChanged;
+			chkStrike.CheckedChanged -= ChkOver_CheckedChanged;
+			fldColor.ColorChanged -= ChkOver_CheckedChanged;
+			cboStyle.SelectedIndexChanged -= ChkOver_CheckedChanged;
+		}
+
+		protected override void AddHandlers()
+		{
+			chkOver.CheckedChanged += ChkOver_CheckedChanged;
+			chkUnder.CheckedChanged += ChkOver_CheckedChanged;
+			chkStrike.CheckedChanged += ChkOver_CheckedChanged;
+			fldColor.ColorChanged += ChkOver_CheckedChanged;
+			cboStyle.SelectedIndexChanged += ChkOver_CheckedChanged;
+		}
+
+		private void ChkOver_CheckedChanged(object sender, EventArgs e)
+		{
+			Save();
+		}
+
+
+		protected override void OnSave()
+		{
+			string line = "";
+			if (chkUnder.Checked)
+			{
+				line = "underline";
+			}
+			if (chkOver.Checked)
+			{
+				line += " overline";
+			}
+			if (chkStrike.Checked)
+			{
+				line += " line-through";
+			}
+			line.Trim();
+			//IE doesn't support shorthand, so leaving out for now since the preview won't show it
+			//string color = ColorTranslator.ToHtml(fldColor.Color);
+			//string style = cboStyle.SelectedItem?.ToString();
+			//Attribute.Value = $"{line} {color} {style}";
+			Attribute.Value = line;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextDecorationControl.resx b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextDecorationControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextDecorationControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextShadowControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextShadowControl.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c436ad60be1f24078e010423b6a2006641a30f34
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextShadowControl.Designer.cs	
@@ -0,0 +1,153 @@
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	partial class StyleTextShadowControl
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.valHoriz = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.valVert = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.skinnedLabel2 = new Desktop.Skinning.SkinnedLabel();
+			this.valBlur = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.fldColor = new Desktop.CommonControls.ColorField();
+			((System.ComponentModel.ISupportInitialize)(this.valHoriz)).BeginInit();
+			((System.ComponentModel.ISupportInitialize)(this.valVert)).BeginInit();
+			((System.ComponentModel.ISupportInitialize)(this.valBlur)).BeginInit();
+			this.SuspendLayout();
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.AutoSize = true;
+			this.skinnedLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.skinnedLabel1.Location = new System.Drawing.Point(3, 3);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(38, 13);
+			this.skinnedLabel1.TabIndex = 0;
+			this.skinnedLabel1.Text = "Offset:";
+			// 
+			// valHoriz
+			// 
+			this.valHoriz.BackColor = System.Drawing.Color.White;
+			this.valHoriz.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valHoriz.ForeColor = System.Drawing.Color.Black;
+			this.valHoriz.Location = new System.Drawing.Point(41, 1);
+			this.valHoriz.Minimum = new decimal(new int[] {
+            100,
+            0,
+            0,
+            -2147483648});
+			this.valHoriz.Name = "valHoriz";
+			this.valHoriz.Size = new System.Drawing.Size(37, 20);
+			this.valHoriz.TabIndex = 1;
+			// 
+			// valVert
+			// 
+			this.valVert.BackColor = System.Drawing.Color.White;
+			this.valVert.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valVert.ForeColor = System.Drawing.Color.Black;
+			this.valVert.Location = new System.Drawing.Point(84, 1);
+			this.valVert.Minimum = new decimal(new int[] {
+            100,
+            0,
+            0,
+            -2147483648});
+			this.valVert.Name = "valVert";
+			this.valVert.Size = new System.Drawing.Size(37, 20);
+			this.valVert.TabIndex = 2;
+			// 
+			// skinnedLabel2
+			// 
+			this.skinnedLabel2.AutoSize = true;
+			this.skinnedLabel2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.skinnedLabel2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel2.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.skinnedLabel2.Location = new System.Drawing.Point(124, 3);
+			this.skinnedLabel2.Name = "skinnedLabel2";
+			this.skinnedLabel2.Size = new System.Drawing.Size(28, 13);
+			this.skinnedLabel2.TabIndex = 3;
+			this.skinnedLabel2.Text = "Blur:";
+			// 
+			// valBlur
+			// 
+			this.valBlur.BackColor = System.Drawing.Color.White;
+			this.valBlur.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valBlur.ForeColor = System.Drawing.Color.Black;
+			this.valBlur.Location = new System.Drawing.Point(152, 1);
+			this.valBlur.Minimum = new decimal(new int[] {
+            100,
+            0,
+            0,
+            -2147483648});
+			this.valBlur.Name = "valBlur";
+			this.valBlur.Size = new System.Drawing.Size(37, 20);
+			this.valBlur.TabIndex = 4;
+			// 
+			// fldColor
+			// 
+			this.fldColor.BackColor = System.Drawing.SystemColors.Control;
+			this.fldColor.Color = System.Drawing.SystemColors.Control;
+			this.fldColor.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+			this.fldColor.Location = new System.Drawing.Point(195, 0);
+			this.fldColor.Name = "fldColor";
+			this.fldColor.Size = new System.Drawing.Size(43, 21);
+			this.fldColor.TabIndex = 5;
+			this.fldColor.UseVisualStyleBackColor = true;
+			// 
+			// StyleTextShadowControl
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.fldColor);
+			this.Controls.Add(this.valBlur);
+			this.Controls.Add(this.skinnedLabel2);
+			this.Controls.Add(this.valVert);
+			this.Controls.Add(this.valHoriz);
+			this.Controls.Add(this.skinnedLabel1);
+			this.Name = "StyleTextShadowControl";
+			this.Size = new System.Drawing.Size(369, 21);
+			((System.ComponentModel.ISupportInitialize)(this.valHoriz)).EndInit();
+			((System.ComponentModel.ISupportInitialize)(this.valVert)).EndInit();
+			((System.ComponentModel.ISupportInitialize)(this.valBlur)).EndInit();
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
+		private Desktop.Skinning.SkinnedNumericUpDown valHoriz;
+		private Desktop.Skinning.SkinnedNumericUpDown valVert;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel2;
+		private Desktop.Skinning.SkinnedNumericUpDown valBlur;
+		private Desktop.CommonControls.ColorField fldColor;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextShadowControl.cs b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextShadowControl.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2a592f2e1661a28cc7b23e547b51b6939f094771
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextShadowControl.cs	
@@ -0,0 +1,73 @@
+using System;
+using System.Drawing;
+
+namespace SPNATI_Character_Editor.Controls.StyleControls
+{
+	[SubAttribute("text-shadow", "Text shadow effect")]
+	public partial class StyleTextShadowControl : SubAttributeControl
+	{
+		public StyleTextShadowControl()
+		{
+			InitializeComponent();
+		}
+
+		protected override void OnBoundData()
+		{
+			string[] pieces = GetPieces();
+			if (pieces.Length > 0)
+			{
+				valHoriz.Value = Math.Min(valHoriz.Maximum, Math.Max(valHoriz.Minimum, (int)PxToInt(pieces[0])));
+			}
+			if (pieces.Length > 1)
+			{
+				valVert.Value = Math.Min(valVert.Maximum, Math.Max(valVert.Minimum, (int)PxToInt(pieces[1])));
+			}
+			if (pieces.Length > 2)
+			{
+				valBlur.Value = Math.Min(valVert.Maximum, Math.Max(valBlur.Minimum, (int)PxToInt(pieces[2])));
+			}
+			if (pieces.Length > 3)
+			{
+				try
+				{
+					Color color = ColorTranslator.FromHtml(pieces[3]);
+					fldColor.Color = color;
+				}
+				catch
+				{
+					fldColor.Color = Color.Black;
+				}
+			}
+		}
+
+		protected override void AddHandlers()
+		{
+			valHoriz.ValueChanged += FieldChanged;
+			valVert.ValueChanged += FieldChanged;
+			valBlur.ValueChanged += FieldChanged;
+			fldColor.ColorChanged += FieldChanged;
+		}
+
+		protected override void RemoveHandlers()
+		{
+			valHoriz.ValueChanged -= FieldChanged;
+			valVert.ValueChanged -= FieldChanged;
+			valBlur.ValueChanged -= FieldChanged;
+			fldColor.ColorChanged -= FieldChanged;
+		}
+
+		private void FieldChanged(object sender, EventArgs e)
+		{
+			Save();
+		}
+
+		protected override void OnSave()
+		{
+			string horiz = IntToPx((int)valHoriz.Value);
+			string vert = IntToPx((int)valVert.Value);
+			string blur = IntToPx((int)valBlur.Value);
+			string color = ColorTranslator.ToHtml(fldColor.Color);
+			Attribute.Value = $"{horiz} {vert} {blur} {color}";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextShadowControl.resx b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextShadowControl.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Controls/StyleControls/StyleTextShadowControl.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Controls/TagControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/TagControl.Designer.cs
deleted file mode 100644
index 694e98d846dd90f44fd4e5f10658eeef27b6926d..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/TagControl.Designer.cs	
+++ /dev/null
@@ -1,85 +0,0 @@
-namespace SPNATI_Character_Editor.Controls
-{
-	partial class TagControl
-	{
-		/// <summary> 
-		/// Required designer variable.
-		/// </summary>
-		private System.ComponentModel.IContainer components = null;
-
-		/// <summary> 
-		/// Clean up any resources being used.
-		/// </summary>
-		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
-		protected override void Dispose(bool disposing)
-		{
-			if (disposing && (components != null))
-			{
-				components.Dispose();
-			}
-			base.Dispose(disposing);
-		}
-
-		#region Component Designer generated code
-
-		/// <summary> 
-		/// Required method for Designer support - do not modify 
-		/// the contents of this method with the code editor.
-		/// </summary>
-		private void InitializeComponent()
-		{
-			this.components = new System.ComponentModel.Container();
-			this.grpBox = new System.Windows.Forms.GroupBox();
-			this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
-			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
-			this.grpBox.SuspendLayout();
-			this.SuspendLayout();
-			// 
-			// grpBox
-			// 
-			this.grpBox.Controls.Add(this.flowPanel);
-			this.grpBox.Dock = System.Windows.Forms.DockStyle.Fill;
-			this.grpBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.grpBox.Location = new System.Drawing.Point(0, 0);
-			this.grpBox.Name = "grpBox";
-			this.grpBox.Size = new System.Drawing.Size(185, 108);
-			this.grpBox.TabIndex = 0;
-			this.grpBox.TabStop = false;
-			this.grpBox.Text = "Group Label";
-			// 
-			// flowPanel
-			// 
-			this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.flowPanel.AutoSize = true;
-			this.flowPanel.BackColor = System.Drawing.SystemColors.Control;
-			this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
-			this.flowPanel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.flowPanel.Location = new System.Drawing.Point(3, 19);
-			this.flowPanel.Name = "flowPanel";
-			this.flowPanel.Padding = new System.Windows.Forms.Padding(2);
-			this.flowPanel.Size = new System.Drawing.Size(179, 83);
-			this.flowPanel.TabIndex = 0;
-			this.flowPanel.WrapContents = false;
-			// 
-			// TagControl
-			// 
-			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.grpBox);
-			this.Name = "TagControl";
-			this.Size = new System.Drawing.Size(185, 108);
-			this.grpBox.ResumeLayout(false);
-			this.grpBox.PerformLayout();
-			this.ResumeLayout(false);
-
-		}
-
-		#endregion
-
-		private System.Windows.Forms.GroupBox grpBox;
-		private System.Windows.Forms.FlowLayoutPanel flowPanel;
-		private System.Windows.Forms.ToolTip toolTip1;
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/TagControl.cs b/editor source/SPNATI Character Editor/Controls/TagControl.cs
deleted file mode 100644
index 46831b1d805315967101c057f6f68646f21e45b1..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Controls/TagControl.cs	
+++ /dev/null
@@ -1,114 +0,0 @@
-using Desktop;
-using System.Collections.Generic;
-using System.Windows.Forms;
-
-namespace SPNATI_Character_Editor.Controls
-{
-	public partial class TagControl : UserControl
-	{
-		private const int TitleHeight = 25;
-
-		private Dictionary<string, Control> _controlMap = new Dictionary<string, Control>();
-
-		public TagControl()
-		{
-			InitializeComponent();
-		}
-
-		/// <summary>
-		/// Builds the checkboxes for the group
-		/// </summary>
-		/// <param name="group"></param>
-		public void SetGroup(TagGroup group, Character character)
-		{
-			string gender = character.Gender;
-			grpBox.Text = group.Label;
-			int width = (flowPanel.Width - (flowPanel.Padding.Left - flowPanel.Padding.Right) * 2 - 5);
-			if (!group.MultiSelect)
-			{
-				Control none = new RadioButton();
-				none.Width = width;
-				none.Margin = new Padding(0);
-				none.Text = "N/A";
-				flowPanel.Controls.Add(none);
-			}
-			foreach (Tag tag in group.Tags)
-			{
-				if (string.IsNullOrEmpty(tag.Gender) || gender == tag.Gender)
-				{
-					Control input = null;
-					if (group.MultiSelect)
-					{
-						input = new CheckBox();
-						input.Text = tag.DisplayName;
-					}
-					else
-					{
-						input = new RadioButton();
-						input.Text = tag.DisplayName;
-					}
-					toolTip1.SetToolTip(input, tag.Description);
-					input.Width = width;
-					input.Margin = new Padding(0);
-					input.Tag = tag;
-					_controlMap[tag.Value] = input;
-					flowPanel.Controls.Add(input);
-				}
-			}
-
-			//size the control to fit its contents
-			Height = TitleHeight + flowPanel.Height;
-		}
-
-		/// <summary>
-		/// Auto-selects any tags from the input list
-		/// </summary>
-		/// <param name="tags"></param>
-		public void CheckTags(List<string> tags)
-		{
-			for (int i = tags.Count - 1; i >= 0; i--)
-			{
-				string tag = tags[i];
-				Control matchingControl = _controlMap.Get(tag);
-				if (matchingControl != null)
-				{
-					if (matchingControl is CheckBox)
-					{
-						((CheckBox)matchingControl).Checked = true;
-					}
-					else if (matchingControl is RadioButton)
-					{
-						((RadioButton)matchingControl).Checked = true;
-					}
-					tags.RemoveAt(i); //remove so that whatever's leftover can go to misc
-				}
-			}
-		}
-
-		/// <summary>
-		/// Gets a list of tags selected
-		/// </summary>
-		/// <returns></returns>
-		public List<string> GetTags()
-		{
-			List<string> tags = new List<string>();
-			foreach (Control ctl in flowPanel.Controls)
-			{
-				if ((ctl is RadioButton && (ctl as RadioButton).Checked) ||
-					(ctl is CheckBox && (ctl as CheckBox).Checked))
-				{
-					Tag tag = ctl.Tag as Tag;
-					if (tag != null)
-					{
-						tags.Add(tag.Value);
-						foreach (string pairedTag in tag.PairedTags)
-						{
-							tags.Add(pairedTag);
-						}
-					}
-				}
-			}
-			return tags;
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Controls/TagGrid.Designer.cs b/editor source/SPNATI Character Editor/Controls/TagGrid.Designer.cs
index 44181639559f57f3de713cbc1195ba12003133d6..8ec1b257e41012a448e03548a2586a59e29cf403 100644
--- a/editor source/SPNATI Character Editor/Controls/TagGrid.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/TagGrid.Designer.cs	
@@ -30,7 +30,7 @@
 		{
 			this.components = new System.ComponentModel.Container();
 			this.panel = new Desktop.CommonControls.SelectablePanel();
-			this.lblGroup = new System.Windows.Forms.Label();
+			this.lblGroup = new Desktop.Skinning.SkinnedLabel();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
 			this.SuspendLayout();
 			// 
@@ -39,8 +39,10 @@
 			this.panel.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.panel.Location = new System.Drawing.Point(0, 0);
 			this.panel.Name = "panel";
+			this.panel.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.panel.Size = new System.Drawing.Size(307, 232);
 			this.panel.TabIndex = 3;
+			this.panel.TabStop = true;
 			this.panel.Paint += new System.Windows.Forms.PaintEventHandler(this.panel_Paint);
 			this.panel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.panel_MouseDown);
 			this.panel.MouseLeave += new System.EventHandler(this.panel_MouseLeave);
@@ -49,6 +51,7 @@
 			// lblGroup
 			// 
 			this.lblGroup.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.lblGroup.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
 			this.lblGroup.Location = new System.Drawing.Point(3, 0);
 			this.lblGroup.Name = "lblGroup";
 			this.lblGroup.Size = new System.Drawing.Size(100, 66);
@@ -72,7 +75,7 @@
 		#endregion
 
 		private Desktop.CommonControls.SelectablePanel panel;
-		private System.Windows.Forms.Label lblGroup;
+		private Desktop.Skinning.SkinnedLabel lblGroup;
 		private System.Windows.Forms.ToolTip toolTip1;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/TagGrid.cs b/editor source/SPNATI Character Editor/Controls/TagGrid.cs
index d905f1d86fb83b86d1e2906b717711d90aef5011..b79391b3d3837552747438196454322d06be347e 100644
--- a/editor source/SPNATI Character Editor/Controls/TagGrid.cs	
+++ b/editor source/SPNATI Character Editor/Controls/TagGrid.cs	
@@ -1,15 +1,17 @@
-using System.Collections.Generic;
+using Desktop.Skinning;
+using System.Collections.Generic;
 using System.Drawing;
 using System.Windows.Forms;
+using System;
 
 namespace SPNATI_Character_Editor.Controls
 {
-	public partial class TagGrid : UserControl
+	public partial class TagGrid : UserControl, ISkinControl
 	{
 		private const int CellSize = 30;
 		private const int HeaderPadding = 10;
 
-		private Pen _border = Pens.DarkGray;
+		private Pen _border = new Pen(Color.DarkGray);
 
 		private BindableTagList _bindings;
 		private Character _character;
@@ -37,6 +39,11 @@ namespace SPNATI_Character_Editor.Controls
 			InitializeComponent();
 		}
 
+		public void OnUpdateSkin(Skin skin)
+		{
+			_border = skin.PrimaryColor.GetBorderPen(VisualState.Normal, false, Enabled);
+		}
+
 		private void ResizeGrid()
 		{
 			int rows = _tags.Count;
@@ -83,11 +90,19 @@ namespace SPNATI_Character_Editor.Controls
 			Graphics g = e.Graphics;
 			StringFormat sf = new StringFormat() { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center };
 
+			Skin skin = SkinManager.Instance.CurrentSkin;
+
 			if (_highlightedCell.X != -1)
 			{
-				g.FillRectangle(Brushes.LightCyan, 0, _headerHeight + _highlightedCell.Y * CellSize, _headerWidth + _highlightedCell.X * CellSize + CellSize, CellSize);
-				g.FillRectangle(Brushes.LightCyan, _headerWidth + _highlightedCell.X * CellSize, _headerHeight, CellSize, _highlightedCell.Y * CellSize);
-				g.FillRectangle(Brushes.LightSkyBlue, _headerWidth + _highlightedCell.X * CellSize, _headerHeight + _highlightedCell.Y * CellSize, CellSize, CellSize);
+				using (SolidBrush lineBrush = new SolidBrush(Color.FromArgb(100, skin.SecondaryLightColor.Normal)))
+				{
+					g.FillRectangle(lineBrush, 0, _headerHeight + _highlightedCell.Y * CellSize, _headerWidth + _layerCount * CellSize + CellSize, CellSize);
+					g.FillRectangle(lineBrush, _headerWidth + _highlightedCell.X * CellSize, _headerHeight, CellSize, _tags.Count * CellSize);
+				}
+				using (SolidBrush pointBrush = new SolidBrush(Color.FromArgb(127, skin.SecondaryColor.Normal)))
+				{
+					g.FillRectangle(pointBrush, _headerWidth + _highlightedCell.X * CellSize, _headerHeight + _highlightedCell.Y * CellSize, CellSize, CellSize);
+				}
 			}
 
 			Image applyIcon = Properties.Resources.ApplyCheckbox;
@@ -113,7 +128,7 @@ namespace SPNATI_Character_Editor.Controls
 				}
 			}
 
-			using (Brush fontBrush = new SolidBrush(this.ForeColor))
+			using (Brush fontBrush = new SolidBrush(skin.Background.ForeColor))
 			{
 				g.DrawLine(_border, 0, _headerHeight, 0, Height);
 				g.DrawLine(_border, _headerWidth + _headerHeight, 0, Width, 0);
@@ -121,7 +136,7 @@ namespace SPNATI_Character_Editor.Controls
 				{
 					int y = _headerHeight + CellSize * i;
 
-					g.DrawString(_tags[i].DisplayName, Font, fontBrush, new Rectangle(HeaderPadding, y + 1, _headerWidth - HeaderPadding * 2, CellSize), sf);
+					g.DrawString(_tags[i].DisplayName, Skin.TextFont, fontBrush, new Rectangle(HeaderPadding, y + 1, _headerWidth - HeaderPadding * 2, CellSize), sf);
 
 					g.DrawLine(_border, 0, y, Width - _headerHeight, y);
 				}
@@ -202,6 +217,16 @@ namespace SPNATI_Character_Editor.Controls
 			}
 			else
 			{
+				if (e.X < _headerWidth && y >= 0 && y < _tags.Count)
+				{
+					Tag tag = _tags[y];
+					toolTip1.Show(tag.Description, panel, new Point(e.X + 5, e.Y + 5));
+				}
+				else
+				{
+					toolTip1.Hide(panel);
+				}
+
 				_highlightedCell.X = -1;
 				_highlightedCell.Y = -1;
 				Cursor = Cursors.Default;
@@ -214,6 +239,7 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void panel_MouseLeave(object sender, System.EventArgs e)
 		{
+			toolTip1.Hide(panel);
 			if (_highlightedCell.X >= 0)
 			{
 				_highlightedCell.X = -1;
diff --git a/editor source/SPNATI Character Editor/Controls/TagList.Designer.cs b/editor source/SPNATI Character Editor/Controls/TagList.Designer.cs
index 2c58c92f3d16814daad5d2bec2b757624a79e704..f37ebfdffc08f83a852a32add5be7f7d2b53ca3c 100644
--- a/editor source/SPNATI Character Editor/Controls/TagList.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/TagList.Designer.cs	
@@ -29,9 +29,9 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			this.grid = new System.Windows.Forms.DataGridView();
+			this.grid = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColTag = new System.Windows.Forms.DataGridViewTextBoxColumn();
-			this.ColStages = new System.Windows.Forms.DataGridViewButtonColumn();
+			this.ColStages = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
 			((System.ComponentModel.ISupportInitialize)(this.grid)).BeginInit();
 			this.SuspendLayout();
@@ -86,9 +86,9 @@
 
 		#endregion
 
-		private System.Windows.Forms.DataGridView grid;
+		private Desktop.Skinning.SkinnedDataGridView grid;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColTag;
-		private System.Windows.Forms.DataGridViewButtonColumn ColStages;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColStages;
 		private System.Windows.Forms.ToolTip toolTip1;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/TreeViews/CaseView.cs b/editor source/SPNATI Character Editor/Controls/TreeViews/CaseView.cs
index 0bc20b2bc666a2301a071c73fd6406f9878b07bc..95ba66802c709bfbacd6de72e2b7de2bc6a0c279 100644
--- a/editor source/SPNATI Character Editor/Controls/TreeViews/CaseView.cs	
+++ b/editor source/SPNATI Character Editor/Controls/TreeViews/CaseView.cs	
@@ -1,9 +1,10 @@
-using SPNATI_Character_Editor.Forms;
+using Desktop.CommonControls;
+using Desktop.Skinning;
+using SPNATI_Character_Editor.Forms;
 using System;
-using System.Collections;
 using System.Collections.Generic;
+using System.Drawing;
 using System.IO;
-using System.Text;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Controls
@@ -13,31 +14,63 @@ namespace SPNATI_Character_Editor.Controls
 	/// </summary>
 	public class CaseView : IDialogueTreeView
 	{
-		public event EventHandler<TreeNode> DeleteNode;
 		public event EventHandler SaveNode;
 
-		/// <summary>
-		/// Mapping from tag to grouper node
-		/// </summary>
-		private Dictionary<string, TreeNode> _tagMap = new Dictionary<string, TreeNode>();
+		private GroupedList<DialogueNode> _model = new GroupedList<DialogueNode>();
+
+		private Font _font = new Font("Arial", 9, FontStyle.Bold);
 
 		/// <summary>
 		/// Mapping from case to node
 		/// </summary>
-		private Dictionary<Case, TreeNode> _caseMap = new Dictionary<Case, TreeNode>();
+		private Dictionary<Case, DialogueNode> _caseMap = new Dictionary<Case, DialogueNode>();
 
-		private TreeView treeDialogue;
+		private AccordionListView _listView;
 		private ContextMenuStrip splitMenu;
 		private Character _character;
 		private CharacterEditorData _editorData;
 		private Func<Case, bool> _filter;
 		private bool _showHidden;
 
-		public void Initialize(TreeView tree, Character character)
+		public void Initialize(AccordionListView listView, Character character)
 		{
-			treeDialogue = tree;
+			_listView = listView;
 			_character = character;
 			_editorData = CharacterDatabase.GetEditorData(character);
+
+			InitializeColumns();
+		}
+
+		private void InitializeColumns()
+		{
+			AccordionColumn column;
+			if (Config.UseSimpleTree)
+			{
+				column = new AccordionColumn("Case", "Label");
+				column.FillWeight = 1;
+				_listView.AddColumn(column);
+			}
+			else
+			{
+				column = new AccordionColumn("Stage", "StageRange");
+				column.Width = 40;
+				_listView.AddColumn(column);
+
+				column = new AccordionColumn("Target", "Target");
+				column.Width = 55;
+				_listView.AddColumn(column);
+
+				column = new AccordionColumn("Conditions", "Conditions");
+				column.FillWeight = 1;
+				_listView.AddColumn(column);
+
+				column = new AccordionColumn("Pri", "Priority");
+				column.Width = 31;
+				column.TextAlign = HorizontalAlignment.Right;
+				_listView.AddColumn(column);
+			}
+
+			_listView.RebuildColumns();
 		}
 
 		public ContextMenuStrip GetCopyMenu()
@@ -52,22 +85,26 @@ namespace SPNATI_Character_Editor.Controls
 			return splitMenu;
 		}
 
+		public void Sort()
+		{
+			_model.SortItems();
+		}
+
 		public void BuildTree(bool showHidden)
 		{
 			_showHidden = showHidden;
-			treeDialogue.Nodes.Clear();
+			_listView.DataSource = null;
 
-			_tagMap = new Dictionary<string, TreeNode>();
+			_model = new GroupedList<DialogueNode>();
+			_model.GroupComparer = SortGroups;
+			_model.ItemComparer = DialogueNode.CompareCases;
 
 			//Make nodes for each case tag
 			foreach (string tag in TriggerDatabase.GetTags())
 			{
 				if (tag == "-") { continue; }
 				Trigger trigger = TriggerDatabase.GetTrigger(tag);
-				TreeNode node = new TreeNode(trigger.Label);
-				node.Tag = tag;
-				treeDialogue.Nodes.Add(node);
-				_tagMap[tag] = node;
+				_model.AddGroup(trigger.Tag);
 			}
 
 			//Add working cases to the right grouper
@@ -78,8 +115,8 @@ namespace SPNATI_Character_Editor.Controls
 				CreateCaseNode(workingCase);
 			}
 
-			treeDialogue.TreeViewNodeSorter = new NodeSorter();
-			treeDialogue.Sort();
+			_model.Sorted = true;
+			_listView.DataSource = _model;
 		}
 
 		private void CreateCaseNode(Case workingCase)
@@ -90,12 +127,9 @@ namespace SPNATI_Character_Editor.Controls
 				return;
 			}
 
-			string tag = workingCase.Tag;
-			TreeNode grouper = _tagMap.Get(tag);
-			if (grouper == null) { return; } //should we display a warning message here? The dialogue is using an unrecognized tag
-
 			DialogueNode wrapper = new DialogueNode(_character, new Stage(workingCase.Stages[0]), workingCase);
-			CreateNode(wrapper, grouper, false);
+			wrapper.Mode = NodeMode.Case;
+			CreateNode(wrapper);
 		}
 
 		/// <summary>
@@ -104,125 +138,10 @@ namespace SPNATI_Character_Editor.Controls
 		/// <param name="wrapper"></param>
 		/// <param name="parent"></param>
 		/// <returns></returns>
-		private TreeNode CreateNode(DialogueNode wrapper, TreeNode parent, bool sorted)
-		{
-			TreeNode node = new TreeNode();
-			node.Tag = wrapper;
-
-			node.ContextMenuStrip = splitMenu;
-			UpdateNodeAppearance(node);
-
-			if (sorted)
-			{
-				bool inserted = false;
-				for (int i = 0; i < parent.Nodes.Count; i++)
-				{
-					DialogueNode sibling = parent.Nodes[i].Tag as DialogueNode;
-					if (Behaviour.CompareTags(wrapper.Case, sibling.Case) <= 0)
-					{
-						parent.Nodes.Insert(i, node);
-						inserted = true;
-						break;
-					}
-				}
-				if (!inserted)
-				{
-					parent.Nodes.Add(node);
-				}
-			}
-			else
-			{
-				parent.Nodes.Add(node);
-			}
-			_caseMap[wrapper.Case] = node;
-			return node;
-		}
-
-		private void UpdateNodeAppearance(TreeNode node)
-		{
-			DialogueNode wrapper = node.Tag as DialogueNode;
-			if (_editorData.IsHidden(wrapper.Case))
-			{
-				node.ForeColor = System.Drawing.Color.LightGray;
-			}
-			else if (wrapper.Case.Hidden == "1")
-			{
-				node.ForeColor = System.Drawing.Color.Gray;
-			}
-			else if (wrapper.Case.HasCollectible)
-			{
-				node.ForeColor = System.Drawing.Color.OrangeRed;
-			}
-			else if (wrapper.Case.HasConditions)
-			{
-				//Highlight targeted dialogue
-				node.ForeColor = System.Drawing.Color.Green;
-			}
-			else
-			{
-				node.ForeColor = System.Drawing.Color.Black;
-				//Highlight lines that are still using the default
-				Tuple<string, string> template = DialogueDatabase.GetTemplate(wrapper.Case.Tag);
-				if (template != null)
-				{
-					foreach (var line in wrapper.Case.Lines)
-					{
-						if (Path.GetFileNameWithoutExtension(line.Image) == template.Item1 && line.Text?.Trim() == template.Item2)
-						{
-							node.ForeColor = System.Drawing.Color.Blue;
-
-							//Color ancestors too
-							TreeNode ancestor = node.Parent;
-							while (ancestor != null)
-							{
-								ancestor.ForeColor = System.Drawing.Color.Blue;
-								ancestor = ancestor.Parent;
-							}
-						}
-					}
-				}
-			}
-
-			//Prefix the case with the stage range			
-			Case c = wrapper.Case;
-			StringBuilder sb = new StringBuilder();
-			if (c.Stages.Count == 0)
-			{
-				sb.Append("???");
-			}
-			else
-			{
-				int last = c.Stages[0];
-				int startRange = last;
-				for (int i = 1; i < c.Stages.Count; i++)
-				{
-					int stage = c.Stages[i];
-					if (stage - 1 > last)
-					{
-						if (startRange == last)
-						{
-							sb.Append(startRange.ToString() + ",");
-						}
-						else
-						{
-							sb.Append($"{startRange}-{last},");
-						}
-						startRange = stage;
-					}
-					last = stage;
-				}
-				if (startRange == last)
-				{
-					sb.Append(startRange.ToString());
-				}
-				else
-				{
-					sb.Append($"{startRange}-{last}");
-				}
-				wrapper.Stage = new Stage(c.Stages[0]);
-			}
-
-			node.Text = $"{sb.ToString()} - {wrapper.ToString()}";
+		private void CreateNode(DialogueNode wrapper)
+		{	
+			_model.AddItem(wrapper);
+			_caseMap[wrapper.Case] = wrapper;
 		}
 
 		public void SetFilter(Func<Case, bool> filter)
@@ -233,28 +152,46 @@ namespace SPNATI_Character_Editor.Controls
 
 		public void SelectNode(int stage, Case stageCase)
 		{
-			TreeNode node = _caseMap.Get(stageCase);
+			DialogueNode node = _caseMap.Get(stageCase);
 			if (node != null)
 			{
-				treeDialogue.SelectedNode = node;
+				_listView.SelectedItem = node;
 			}
 		}
 
-		public string AddingCase()
+		public string AddingCase(out string folder)
 		{
-			DialogueNode node = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			folder = "";
 			if (_character == null)
 			{
 				return null;
 			}
+			DialogueNode node = _listView.SelectedItem as DialogueNode;
 			if (node == null)
 			{
-				string tag = treeDialogue.SelectedNode?.Tag?.ToString();
+				GroupedListGrouper group = _listView.SelectedItem as GroupedListGrouper;
+				string tag = group.RootKey ?? group?.Key?.ToString();
 				if (!string.IsNullOrEmpty(tag))
 				{
+					if (tag != group?.Key?.ToString())
+					{
+						folder = group?.Key?.ToString();
+					}
 					return tag;
 				}
 			}
+			else
+			{
+				Case c = node?.Case;
+				if (c != null && _editorData != null)
+				{
+					CaseLabel label = _editorData.GetLabel(c);
+					if (label != null)
+					{
+						folder = label.Folder;
+					}
+				}
+			}
 
 			return node?.Case?.Tag;
 		}
@@ -262,71 +199,34 @@ namespace SPNATI_Character_Editor.Controls
 		public void ModifyCase(Case modifiedCase)
 		{
 			//since all stages are in the same node, we don't need to do anything but update that node's appearance!
-			TreeNode node = _caseMap.Get(modifiedCase);
+			DialogueNode node = _caseMap.Get(modifiedCase);
 			if (node != null)
 			{
-				UpdateNodeAppearance(node);
+				if (modifiedCase.Stages.Count > 0)
+				{
+					node.Stage = new Stage(modifiedCase.Stages[0]);
+				}
 			}
 		}
 
 		public void AddCase(Case newCase)
 		{
-			TreeNode grouper = _tagMap.Get(newCase.Tag);
 			DialogueNode wrapper = new DialogueNode(_character, new Stage(newCase.Stages[0]), newCase);
-			CreateNode(wrapper, grouper, false);
+			wrapper.Mode = NodeMode.Case;
+			CreateNode(wrapper);
 		}
 
 		public void RemoveCase(Case removedCase)
 		{
-			TreeNode node = _caseMap.Get(removedCase);
+			DialogueNode node = _caseMap.Get(removedCase);
 			if (node == null) { return; }
-			//TreeView will crash if trying to delete the node that is being unselected, so delay it to let the stack unwind
-			DeleteNode?.Invoke(this, node);
+			_model.RemoveItem(node);
 			_caseMap.Remove(removedCase);
 		}
-
-		private class NodeSorter : IComparer
-		{
-			public int Compare(object x, object y)
-			{
-				TreeNode node1 = x as TreeNode;
-				TreeNode node2 = y as TreeNode;
-
-				DialogueNode caseNode1 = node1.Tag as DialogueNode;
-				DialogueNode caseNode2 = node2.Tag as DialogueNode;
-				if (caseNode1 != null)
-				{
-					//case nodes
-					int stage1 = caseNode1.Case.Stages.Count > 0 ? caseNode1.Case.Stages[0] : -1;
-					int stage2 = caseNode2.Case.Stages.Count > 0 ? caseNode2.Case.Stages[0] : -1;
-					int diff = stage1.CompareTo(stage2);
-					if (diff == 0)
-					{
-						diff = caseNode1.Case.Stages[caseNode1.Case.Stages.Count - 1].CompareTo(caseNode2.Case.Stages[caseNode2.Case.Stages.Count - 1]);
-						if (diff == 0)
-						{
-							diff = caseNode2.Case.GetPriority().CompareTo(caseNode1.Case.GetPriority());
-							if (diff == 0)
-							{
-								diff = node1.Text.CompareTo(node2.Text);
-							}
-						}
-					}
-					return diff;
-				}
-				else
-				{
-					//groupers
-					string t1 = (string)node1.Tag;
-					string t2 = (string)node2.Tag;
-					return TriggerDatabase.Compare(t1, t2);
-				}
-			}
-		}
-
+		
 		private void SeparateCaseFromStage(object sender, EventArgs e)
 		{
-			DialogueNode selectedNode = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode selectedNode = _listView.SelectedItem as DialogueNode;
 			if (_character == null || selectedNode?.Case == null)
 				return;
 			Case selectedCase = selectedNode?.Case;
@@ -343,7 +243,7 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void SplitCaseAtPoint(object sender, EventArgs e)
 		{
-			DialogueNode selectedNode = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode selectedNode = _listView.SelectedItem as DialogueNode;
 			if (_character == null || selectedNode?.Case == null)
 				return;
 			Case selectedCase = selectedNode?.Case;
@@ -363,7 +263,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		private void SplitAllStages(object sender, EventArgs e)
 		{
-			DialogueNode selectedNode = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode selectedNode = _listView.SelectedItem as DialogueNode;
 			if (_character == null || selectedNode?.Case == null)
 				return;
 			int stage = selectedNode.Stage.Id;
@@ -375,13 +275,13 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void DuplicateCase(object sender, EventArgs e)
 		{
-			DialogueNode selectedNode = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode selectedNode = _listView.SelectedItem as DialogueNode;
 			if (_character == null || selectedNode?.Case == null)
 				return;
 			Case selectedCase = selectedNode.Case;
 			int stage = selectedNode.Stage.Id;
 			SaveNode?.Invoke(this, EventArgs.Empty);
-			Case copy = _character.Behavior.DuplicateCase(selectedCase);
+			Case copy = _character.Behavior.DuplicateCase(selectedCase, true);
 			SelectNode(stage, copy);
 		}
 
@@ -390,7 +290,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		private void DeleteCase(object sender, EventArgs e)
 		{
-			DialogueNode selectedNode = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode selectedNode = _listView.SelectedItem as DialogueNode;
 			if (_character == null || selectedNode?.Case == null)
 				return;
 
@@ -410,16 +310,12 @@ namespace SPNATI_Character_Editor.Controls
 		{
 			if (hide)
 			{
-				TreeNode node = _caseMap.Get(c);
+				DialogueNode node = _caseMap.Get(c);
 				if (node != null)
 				{
-					if (_showHidden)
+					if (!_showHidden)
 					{
-						UpdateNodeAppearance(node);
-					}
-					else
-					{
-						node.Remove();
+						_model.RemoveItem(node);
 						_caseMap.Remove(c);
 					}
 				}
@@ -428,10 +324,10 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				if (_showHidden)
 				{
-					TreeNode node = _caseMap.Get(c);
+					DialogueNode node = _caseMap.Get(c);
 					if (node != null)
 					{
-						UpdateNodeAppearance(node);
+						node.Dummy++;
 					}
 				}
 				else
@@ -441,5 +337,91 @@ namespace SPNATI_Character_Editor.Controls
 				}
 			}
 		}
+
+		public int SortGroups(string key1, string key2)
+		{
+			return TriggerDatabase.Compare(key1, key2);
+		}
+
+		public void FormatRow(FormatRowEventArgs args)
+		{
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			DialogueNode node = args.Model as DialogueNode;
+			args.Tooltip = node.Case.ToString();
+
+			CaseLabel label = _editorData.GetLabel(node.Case);
+			if (label != null)
+			{
+				ColorCode colorCode = Definitions.Instance.Get<ColorCode>(label.ColorCode);
+				if (colorCode != null)
+				{
+					args.ForeColor = colorCode.GetColor();
+					return;
+				}
+			}
+			args.GrouperColor = GetGroupColor(args.Group.Key, args.Group.Index);
+
+			if (_editorData.IsHidden(node.Case))
+			{
+				args.ForeColor = skin.LightGray;
+			}
+			else if (node.Case.Hidden == "1")
+			{
+				args.ForeColor = skin.Gray;
+			}
+			else if (node.Case.HasCollectible)
+			{
+				args.ForeColor = skin.Orange;
+			}
+			else if ((Config.ColorTargetedLines || Config.UseSimpleTree) && node.Case.HasTargetedConditions)
+			{
+				args.ForeColor = skin.Green;
+			}
+			else
+			{
+				//Highlight lines that are still using the default
+				Tuple<string, string> template = DialogueDatabase.GetTemplate(node.Case.Tag);
+				if (template != null)
+				{
+					foreach (var line in node.Case.Lines)
+					{
+						if (Path.GetFileNameWithoutExtension(line.Image) == template.Item1 && line.Text?.Trim() == template.Item2)
+						{
+							args.ForeColor = skin.Blue;
+						}
+					}
+				}
+			}
+		}
+
+		private Color GetGroupColor(string key, int index)
+		{
+			Trigger trigger = TriggerDatabase.GetTrigger(key);
+			if (string.IsNullOrEmpty(trigger.Label))
+			{
+				return index % 2 == 0 ? SkinManager.Instance.CurrentSkin.PrimaryForeColor : SkinManager.Instance.CurrentSkin.SecondaryForeColor;
+			}
+			return SkinManager.Instance.CurrentSkin.GetGrouper(trigger.ColorScheme);
+		}
+
+		public void FormatGroup(FormatGroupEventArgs args)
+		{
+			args.Font = _font;
+			args.ForeColor = GetGroupColor(args.Group.Key, args.Group.Index);
+			Trigger trigger = TriggerDatabase.GetTrigger(args.Group.Key);
+			if (!string.IsNullOrEmpty(trigger.Label))
+			{
+				args.Label = trigger.Label;
+			}
+		}
+
+		public ContextMenuStrip ShowContextMenu(AccordionListViewEventArgs args)
+		{
+			if (args.Model != null)
+			{
+				return splitMenu;
+			}
+			return null;
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/TreeViews/IDialogueTreeView.cs b/editor source/SPNATI Character Editor/Controls/TreeViews/IDialogueTreeView.cs
index d0fa4741ac94e297fe90a3937d38c0bf419d54cc..052842f2d5f0b4198768db4e80d6cd5b9dc419bb 100644
--- a/editor source/SPNATI Character Editor/Controls/TreeViews/IDialogueTreeView.cs	
+++ b/editor source/SPNATI Character Editor/Controls/TreeViews/IDialogueTreeView.cs	
@@ -1,4 +1,5 @@
-using System;
+using Desktop.CommonControls;
+using System;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Controls
@@ -8,10 +9,6 @@ namespace SPNATI_Character_Editor.Controls
 	/// </summary>
 	public interface IDialogueTreeView
 	{
-		/// <summary>
-		/// Informs the host that it should delete a node
-		/// </summary>
-		event EventHandler<TreeNode> DeleteNode;
 		/// <summary>
 		/// Informs the host that it should save a node
 		/// </summary>
@@ -22,7 +19,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		/// <param name="tree">The tree control this is affecting</param>
 		/// <param name="character">The character whose data should be populated</param>
-		void Initialize(TreeView tree,  Character character);
+		void Initialize(AccordionListView listView, Character character);
 
 		/// <summary>
 		/// Gets the context menu to use for the Copy Tools menu
@@ -58,7 +55,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// Called when the Add button is clicked
 		/// </summary>
 		/// <returns>A tag for a new case to add, "" to open the Add dropdown, or null to do nothing</returns>
-		string AddingCase();
+		string AddingCase(out string folder);
 
 		/// <summary>
 		/// Adds a brand new case to the tree
@@ -83,6 +80,13 @@ namespace SPNATI_Character_Editor.Controls
 		/// <param name="theCase"></param>
 		/// <param name="hide"></param>
 		void HideCase(Case theCase, bool hide);
+
+		int SortGroups(string key1, string key2);
+		void FormatRow(FormatRowEventArgs args);
+		void FormatGroup(FormatGroupEventArgs args);
+		void Sort();
+
+		ContextMenuStrip ShowContextMenu(AccordionListViewEventArgs args);
 	}
 
 	public enum TreeFilterMode
diff --git a/editor source/SPNATI Character Editor/Controls/TreeViews/StageView.cs b/editor source/SPNATI Character Editor/Controls/TreeViews/StageView.cs
index fe15cac2e5f8c6161003737a259e3f3c517384b0..df64bd7fad8b8715c5ebd93dd19ba651bdf77e92 100644
--- a/editor source/SPNATI Character Editor/Controls/TreeViews/StageView.cs	
+++ b/editor source/SPNATI Character Editor/Controls/TreeViews/StageView.cs	
@@ -1,6 +1,9 @@
 using Desktop;
+using Desktop.CommonControls;
+using Desktop.Skinning;
 using System;
 using System.Collections.Generic;
+using System.Drawing;
 using System.IO;
 using System.Windows.Forms;
 
@@ -11,32 +14,63 @@ namespace SPNATI_Character_Editor.Controls
 	/// </summary>
 	public class StageView : IDialogueTreeView
 	{
-		public event EventHandler<TreeNode> DeleteNode;
 		public event EventHandler SaveNode;
 
-		/// <summary>
-		/// Mapping from stage to node
-		/// </summary>
-		private Dictionary<int, TreeNode> _stageMap = new Dictionary<int, TreeNode>();
+		private Font _font = new Font("Arial", 9, FontStyle.Bold);
+		private GroupedList<DialogueNode> _model = new GroupedList<DialogueNode>();
 
 		/// <summary>
 		/// Mapping from stage+case to node
 		/// </summary>
-		private DualKeyDictionary<Case, int, TreeNode> _caseMap = new DualKeyDictionary<Case, int, TreeNode>();
+		private DualKeyDictionary<Case, int, DialogueNode> _caseMap = new DualKeyDictionary<Case, int, DialogueNode>();
 
-		private TreeView treeDialogue;
+		private AccordionListView _listView;
 		private ContextMenuStrip splitMenu;
 		private Character _character;
 		private CharacterEditorData _editorData;
 		private Func<Case, bool> _filter;
 		private bool _showHidden;
 
-		public void Initialize(TreeView tree, Character character)
+		public void Initialize(AccordionListView listView, Character character)
 		{
-			treeDialogue = tree;
-			treeDialogue.TreeViewNodeSorter = null;
+			_listView = listView;
 			_character = character;
 			_editorData = CharacterDatabase.GetEditorData(_character);
+
+			InitializeColumns();
+		}
+
+		private void InitializeColumns()
+		{
+			AccordionColumn column;
+
+			if (Config.UseSimpleTree)
+			{
+				column = new AccordionColumn("Case", "Label");
+				column.FillWeight = 1;
+				_listView.AddColumn(column);
+			}
+			else
+			{
+				column = new AccordionColumn("Tag", "Tag");
+				column.Width = 160;
+				_listView.AddColumn(column);
+
+				column = new AccordionColumn("Tgt", "Target");
+				column.Width = 40;
+				_listView.AddColumn(column);
+
+				column = new AccordionColumn("Conditions", "Conditions");
+				column.FillWeight = 1;
+				_listView.AddColumn(column);
+
+				column = new AccordionColumn("P", "Priority");
+				column.Width = 31;
+				column.TextAlign = HorizontalAlignment.Right;
+				_listView.AddColumn(column);
+			}
+
+			_listView.RebuildColumns();
 		}
 
 		public ContextMenuStrip GetCopyMenu()
@@ -53,19 +87,24 @@ namespace SPNATI_Character_Editor.Controls
 			return splitMenu;
 		}
 
+		public void Sort()
+		{
+			_model.SortItems();
+		}
+
 		public void BuildTree(bool showHidden)
 		{
 			_showHidden = showHidden;
-			treeDialogue.Nodes.Clear();
-			treeDialogue.Sorted = false;
+			_listView.DataSource = null;
 
-			_stageMap = new Dictionary<int, TreeNode>();
+			_model = new GroupedList<DialogueNode>();
+			_model.GroupComparer = SortGroups;
+			_model.ItemComparer = DialogueNode.CompareCases;
 
-			//Make nodes for each stage
+			//Make groups for each stage (in case no lines exist for that stage)
 			for (int i = 0; i < _character.Layers + Clothing.ExtraStages; i++)
 			{
-				TreeNode node = CreateNode(new DialogueNode(_character, new Stage(i)), null, false);
-				_stageMap[i] = node;
+				_model.AddGroup(i.ToString());
 			}
 
 			//Make nodes for cases
@@ -83,126 +122,29 @@ namespace SPNATI_Character_Editor.Controls
 				}
 
 				//create a node for each stage the appears in
-				GenerateNodes(workingCase, false);
+				GenerateNodes(workingCase);
 			}
+
+			_model.Sorted = true;
+			_listView.DataSource = _model;
 		}
 
-		private void GenerateNodes(Case workingCase, bool sorted)
+		private void GenerateNodes(Case workingCase)
 		{
 			foreach (int stageIndex in workingCase.Stages)
 			{
-				GenerateNode(workingCase, stageIndex, sorted);
+				GenerateNode(workingCase, stageIndex);
 			}
 		}
 
-		private void GenerateNode(Case workingCase, int stageIndex, bool sorted)
+		private void GenerateNode(Case workingCase, int stageIndex)
 		{
-			TreeNode stageNode = _stageMap.Get(stageIndex);
-			if (stageNode == null) { return; }
-
-			Stage stage = (stageNode.Tag as DialogueNode).Stage;
+			Stage stage = new Stage(stageIndex);
 
-			TreeNode caseNode = CreateNode(new DialogueNode(_character, stage, workingCase), stageNode, sorted);
-			_caseMap.Set(workingCase, stageIndex, caseNode);
-		}
-
-		/// <summary>
-		/// Creates a node in the dialogue tree
-		/// </summary>
-		/// <param name="wrapper"></param>
-		/// <param name="parent"></param>
-		/// <returns></returns>
-		private TreeNode CreateNode(DialogueNode wrapper, TreeNode parent, bool sorted)
-		{
-			TreeNode node = new TreeNode();
-			node.Text = wrapper.ToString();
-			node.Tag = wrapper;
-
-			if (wrapper.NodeType == NodeType.Case)
-			{
-				node.ContextMenuStrip = splitMenu;
-				UpdateNodeAppearance(node);
-			}
-
-			if (parent == null)
-			{
-				treeDialogue.Nodes.Add(node);
-			}
-			else
-			{
-				if (sorted)
-				{
-					bool inserted = false;
-					for (int i = 0; i < parent.Nodes.Count; i++)
-					{
-						DialogueNode sibling = parent.Nodes[i].Tag as DialogueNode;
-						if (Behaviour.CompareTags(wrapper.Case, sibling.Case) <= 0)
-						{
-							parent.Nodes.Insert(i, node);
-							inserted = true;
-							break;
-						}
-					}
-					if (!inserted)
-					{
-						parent.Nodes.Add(node);
-					}
-				}
-				else
-				{
-					parent.Nodes.Add(node);
-				}
-			}
-			return node;
-		}
-
-		private void UpdateNodeAppearance(TreeNode node)
-		{
-			DialogueNode wrapper = node.Tag as DialogueNode;
-			if (_editorData.IsHidden(wrapper.Case))
-			{
-				node.ForeColor = System.Drawing.Color.LightGray;
-			}
-			else if (wrapper.Case.Hidden == "1")
-			{
-				node.ForeColor = System.Drawing.Color.Gray;
-			}
-			else if (wrapper.Case.HasCollectible)
-			{
-				node.ForeColor = System.Drawing.Color.OrangeRed;
-			}
-			else if (wrapper.Case.HasConditions)
-			{
-				//Highlight targeted dialogue
-				node.ForeColor = System.Drawing.Color.Green;
-			}
-			else
-			{
-				node.ForeColor = System.Drawing.Color.Black;
-				//Highlight lines that are still using the default
-				Tuple<string, string> template = DialogueDatabase.GetTemplate(wrapper.Case.Tag);
-				if (template != null)
-				{
-					foreach (var line in wrapper.Case.Lines)
-					{
-						if (Path.GetFileNameWithoutExtension(line.Image) == template.Item1 && line.Text?.Trim() == template.Item2)
-						{
-							node.ForeColor = System.Drawing.Color.Blue;
-
-							//Color ancestors too
-							TreeNode ancestor = node.Parent;
-							while (ancestor != null)
-							{
-								ancestor.ForeColor = System.Drawing.Color.Blue;
-								ancestor = ancestor.Parent;
-							}
-						}
-					}
-				}
-			}
-
-			//update text as well
-			node.Text = wrapper.Case.ToString();
+			DialogueNode node = new DialogueNode(_character, stage, workingCase);
+			node.Mode = NodeMode.Stage;
+			_caseMap.Set(workingCase, stageIndex, node);
+			_model.AddItem(node);
 		}
 
 		public void SetFilter(Func<Case, bool> filter)
@@ -213,27 +155,39 @@ namespace SPNATI_Character_Editor.Controls
 
 		public void SelectNode(int stage, Case stageCase)
 		{
-			TreeNode node = _caseMap.Get(stageCase, stage);
+			DialogueNode node = _caseMap.Get(stageCase, stage);
 			if (node != null)
 			{
-				treeDialogue.SelectedNode = node;
+				_listView.SelectedItem = node;
+			}
+			else
+			{
+				//try the earliest stage
+				if (stageCase.Stages.Count > 0 && stage != stageCase.Stages[0])
+				{
+					SelectNode(stageCase.Stages[0], stageCase);
+				}
 			}
 		}
 
-		public string AddingCase()
+		public string AddingCase(out string folder)
 		{
-			DialogueNode node = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			folder = "";
+			DialogueNode node = _listView.SelectedItem as DialogueNode;
 			if (_character == null || node == null)
 			{
 				return null;
 			}
-
-			if (node.Case == null)
+			Case c = node?.Case;
+			if (c != null && _editorData != null)
 			{
-				return "";
+				CaseLabel label = _editorData.GetLabel(c);
+				if (label != null)
+				{
+					folder = label.Folder;
+				}
 			}
-
-			return node?.Case?.Tag;
+			return c?.Tag;
 		}
 
 		public void ModifyCase(Case modifiedCase)
@@ -252,51 +206,45 @@ namespace SPNATI_Character_Editor.Controls
 			}
 
 			//Process stages that no longer exist
-			List<Tuple<int, TreeNode>> nodesToRemove = new List<Tuple<int, TreeNode>>();
-			foreach (KeyValuePair<int, TreeNode> kvp in _caseMap[modifiedCase])
+			List<Tuple<int, DialogueNode>> nodesToRemove = new List<Tuple<int, DialogueNode>>();
+			foreach (KeyValuePair<int, DialogueNode> kvp in _caseMap[modifiedCase])
 			{
 				int stage = kvp.Key;
 				modifiedStages.Remove(stage);
 				if (!modifiedCase.Stages.Contains(stage))
 				{
-					nodesToRemove.Add(new Tuple<int, TreeNode>(kvp.Key, kvp.Value));
-				}
-				else
-				{
-					UpdateNodeAppearance(kvp.Value);
+					nodesToRemove.Add(new Tuple<int, DialogueNode>(kvp.Key, kvp.Value));
 				}
 			}
-			foreach (Tuple<int, TreeNode> kvp in nodesToRemove)
+			foreach (Tuple<int, DialogueNode> kvp in nodesToRemove)
 			{
-				//TreeView will crash if trying to delete the node that is being unselected, so delay it to let the stack unwind
-				DeleteNode?.Invoke(this, kvp.Item2);
+				_model.RemoveItem(kvp.Item2);
 				_caseMap.Remove(modifiedCase, kvp.Item1);
 			}
 
 			//Process stages that don't have nodes yet
 			foreach (int stage in modifiedStages)
 			{
-				GenerateNode(modifiedCase, stage, true);
+				GenerateNode(modifiedCase, stage);
 			}
 		}
 
 		public void AddCase(Case newCase)
 		{
-			GenerateNodes(newCase, true);
+			GenerateNodes(newCase);
 		}
 
 		public void RemoveCase(Case removedCase)
 		{
 			if (!_caseMap.ContainsPrimaryKey(removedCase)) { return; }
-			List<Tuple<int, TreeNode>> nodesToRemove = new List<Tuple<int, TreeNode>>();
-			foreach (KeyValuePair<int, TreeNode> kvp in _caseMap[removedCase])
+			List<Tuple<int, DialogueNode>> nodesToRemove = new List<Tuple<int, DialogueNode>>();
+			foreach (KeyValuePair<int, DialogueNode> kvp in _caseMap[removedCase])
 			{
-				nodesToRemove.Add(new Tuple<int, TreeNode>(kvp.Key, kvp.Value));
+				nodesToRemove.Add(new Tuple<int, DialogueNode>(kvp.Key, kvp.Value));
 			}
-			foreach (Tuple<int, TreeNode> kvp in nodesToRemove)
+			foreach (Tuple<int, DialogueNode> kvp in nodesToRemove)
 			{
-				//TreeView will crash if trying to delete the node that is being unselected, so delay it to let the stack unwind
-				DeleteNode?.Invoke(this, kvp.Item2);
+				_model.RemoveItem(kvp.Item2);
 			}
 			_caseMap.Remove(removedCase);
 		}
@@ -306,7 +254,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		private void SplitAllStages(object sender, EventArgs e)
 		{
-			DialogueNode selectedNode = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode selectedNode = _listView.SelectedItem as DialogueNode;
 			if (_character == null || selectedNode?.Case == null)
 				return;
 			int stage = selectedNode.Stage.Id;
@@ -321,7 +269,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		private void SeparateCaseFromStage(object sender, EventArgs e)
 		{
-			DialogueNode selectedNode = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode selectedNode = _listView.SelectedItem as DialogueNode;
 			if (_character == null || selectedNode?.Case == null)
 				return;
 			int stage = selectedNode.Stage.Id;
@@ -336,7 +284,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		private void SplitCaseAtPoint(object sender, EventArgs e)
 		{
-			DialogueNode selectedNode = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode selectedNode = _listView.SelectedItem as DialogueNode;
 			if (_character == null || selectedNode?.Case == null)
 				return;
 			int stage = selectedNode.Stage.Id;
@@ -351,7 +299,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// </summary>
 		private void DeleteCase(object sender, EventArgs e)
 		{
-			DialogueNode selectedNode = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode selectedNode = _listView.SelectedItem as DialogueNode;
 			if (_character == null || selectedNode?.Case == null)
 				return;
 
@@ -364,19 +312,19 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void DuplicateCase(object sender, EventArgs e)
 		{
-			DialogueNode selectedNode = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode selectedNode = _listView.SelectedItem as DialogueNode;
 			if (_character == null || selectedNode?.Case == null)
 				return;
 			Case selectedCase = selectedNode.Case;
 			int stage = selectedNode.Stage.Id;
 			SaveNode?.Invoke(this, EventArgs.Empty);
-			Case copy = _character.Behavior.DuplicateCase(selectedCase);
+			Case copy = _character.Behavior.DuplicateCase(selectedCase, true);
 			SelectNode(stage, copy);
 		}
 
 		private void BulkReplace(object sender, EventArgs e)
 		{
-			DialogueNode selectedNode = treeDialogue.SelectedNode?.Tag as DialogueNode;
+			DialogueNode selectedNode = _listView.SelectedItem as DialogueNode;
 			if (selectedNode?.Case == null)
 				return;
 			Case selectedCase = selectedNode.Case;
@@ -406,16 +354,12 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				foreach (int stage in c.Stages)
 				{
-					TreeNode node = _caseMap.Get(c, stage);
+					DialogueNode node = _caseMap.Get(c, stage);
 					if (node != null)
 					{
-						if (_showHidden)
+						if (!_showHidden)
 						{
-							UpdateNodeAppearance(node);
-						}
-						else
-						{
-							node.Remove();
+							_model.RemoveItem(node);
 							_caseMap.Remove(c, stage);
 						}
 					}
@@ -423,23 +367,98 @@ namespace SPNATI_Character_Editor.Controls
 			}
 			else
 			{
-				foreach (int stage in c.Stages)
+				if (_showHidden)
 				{
-					if (_showHidden)
+					foreach (int stage in c.Stages)
 					{
-						//just update color since the node already exists
-						TreeNode node = _caseMap.Get(c, stage);
+						DialogueNode node = _caseMap.Get(c, stage);
 						if (node != null)
 						{
-							UpdateNodeAppearance(node);
+							node.Dummy++;
 						}
 					}
-					else
+				}
+			}
+		}
+
+		public int SortGroups(string key1, string key2)
+		{
+			int s1 = int.Parse(key1);
+			int s2 = int.Parse(key2);
+			return s1.CompareTo(s2);
+		}
+
+		public void FormatRow(FormatRowEventArgs args)
+		{
+			DialogueNode node = args.Model as DialogueNode;
+			Skin skin = SkinManager.Instance.CurrentSkin;
+			args.Tooltip = node.Case.ToString();
+
+			CaseLabel label = _editorData.GetLabel(node.Case);
+			if (label != null)
+			{
+				ColorCode colorCode = Definitions.Instance.Get<ColorCode>(label.ColorCode);
+				if (colorCode != null)
+				{
+					args.ForeColor = colorCode.GetColor();
+					return;
+				}
+			}
+
+			if (_editorData.IsHidden(node.Case))
+			{
+				args.ForeColor = skin.LightGray;
+			}
+			else if (node.Case.Hidden == "1")
+			{
+				args.ForeColor = skin.Gray;
+			}
+			else if (node.Case.HasCollectible)
+			{
+				args.ForeColor = skin.Orange;
+			}
+			else if ((Config.ColorTargetedLines || Config.UseSimpleTree) && node.Case.HasTargetedConditions)
+			{
+				args.ForeColor = skin.Green;
+			}
+			else
+			{
+				//Highlight lines that are still using the default
+				Tuple<string, string> template = DialogueDatabase.GetTemplate(node.Case.Tag);
+				if (template != null)
+				{
+					foreach (var line in node.Case.Lines)
 					{
-						//wait, how would you even unhide a node if it's not in the tree already? just ignore implementing this for now
+						if ((Path.GetFileNameWithoutExtension(line.Image) == template.Item1 || string.IsNullOrEmpty(line.Image)) && line.Text?.Trim() == template.Item2)
+						{
+							args.ForeColor = skin.Blue;
+						}
 					}
 				}
 			}
 		}
+
+		public void FormatGroup(FormatGroupEventArgs args)
+		{
+			args.Font = _font;
+			int stage;
+			if (int.TryParse(args.Group.Key, out stage))
+			{
+				args.Label = _character.LayerToStageName(stage).DisplayName + " (" + stage + ")";
+			}
+			else
+			{
+				args.Label = args.Group.Key;
+			}
+		}
+
+		public ContextMenuStrip ShowContextMenu(AccordionListViewEventArgs args)
+		{
+			if (args.Model != null)
+			{
+				return splitMenu;
+			}
+			return null;
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/ValidationControl.Designer.cs b/editor source/SPNATI Character Editor/Controls/ValidationControl.Designer.cs
index d7ff7038e21356e65e1d490bb4b560504d422b9f..27480cd7f5b6e9783fbde11c25144d8671b2c9fb 100644
--- a/editor source/SPNATI Character Editor/Controls/ValidationControl.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Controls/ValidationControl.Designer.cs	
@@ -29,74 +29,60 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			this.label3 = new System.Windows.Forms.Label();
-			this.lstFilters = new System.Windows.Forms.ListBox();
-			this.label2 = new System.Windows.Forms.Label();
-			this.label1 = new System.Windows.Forms.Label();
-			this.lstCharacters = new System.Windows.Forms.ListBox();
-			this.lstWarnings = new System.Windows.Forms.ListBox();
-			this.pnlValid = new System.Windows.Forms.Panel();
-			this.label4 = new System.Windows.Forms.Label();
-			this.pnlWarnings = new System.Windows.Forms.Panel();
-			this.cmdCopy = new System.Windows.Forms.Button();
-			this.cmdGoTo = new System.Windows.Forms.Button();
-			this.pnlProgress = new System.Windows.Forms.Panel();
-			this.lblProgress = new System.Windows.Forms.Label();
-			this.progressBar = new System.Windows.Forms.ProgressBar();
+			this.lstFilters = new Desktop.Skinning.SkinnedListBox();
+			this.lstCharacters = new Desktop.Skinning.SkinnedListBox();
+			this.lstWarnings = new Desktop.Skinning.SkinnedListBox();
+			this.pnlValid = new Desktop.Skinning.SkinnedPanel();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
+			this.pnlWarnings = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.cmdGoTo = new Desktop.Skinning.SkinnedButton();
+			this.cmdCopy = new Desktop.Skinning.SkinnedButton();
+			this.cmdCopyAll = new Desktop.Skinning.SkinnedButton();
+			this.pnlProgress = new Desktop.Skinning.SkinnedPanel();
+			this.lblProgress = new Desktop.Skinning.SkinnedLabel();
+			this.progressBar = new Desktop.Skinning.SkinnedProgressBar();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
-			this.cmdCopyAll = new System.Windows.Forms.Button();
+			this.skinnedGroupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.skinnedGroupBox2 = new Desktop.Skinning.SkinnedGroupBox();
+			this.skinnedGroupBox3 = new Desktop.Skinning.SkinnedGroupBox();
 			this.pnlValid.SuspendLayout();
 			this.pnlWarnings.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
 			this.pnlProgress.SuspendLayout();
+			this.skinnedGroupBox1.SuspendLayout();
+			this.skinnedGroupBox2.SuspendLayout();
+			this.skinnedGroupBox3.SuspendLayout();
 			this.SuspendLayout();
 			// 
-			// label3
-			// 
-			this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-			this.label3.AutoSize = true;
-			this.label3.Location = new System.Drawing.Point(3, 520);
-			this.label3.Name = "label3";
-			this.label3.Size = new System.Drawing.Size(34, 13);
-			this.label3.TabIndex = 13;
-			this.label3.Text = "Filters";
-			// 
 			// lstFilters
 			// 
-			this.lstFilters.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.lstFilters.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstFilters.BackColor = System.Drawing.Color.White;
+			this.lstFilters.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstFilters.ForeColor = System.Drawing.Color.Black;
 			this.lstFilters.FormattingEnabled = true;
-			this.lstFilters.Location = new System.Drawing.Point(3, 536);
+			this.lstFilters.Location = new System.Drawing.Point(6, 24);
 			this.lstFilters.Name = "lstFilters";
 			this.lstFilters.SelectionMode = System.Windows.Forms.SelectionMode.MultiSimple;
-			this.lstFilters.Size = new System.Drawing.Size(215, 134);
+			this.lstFilters.Size = new System.Drawing.Size(200, 108);
 			this.lstFilters.TabIndex = 12;
 			this.lstFilters.SelectedIndexChanged += new System.EventHandler(this.lstFilters_SelectedIndexChanged);
 			// 
-			// label2
-			// 
-			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(221, 5);
-			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(52, 13);
-			this.label2.TabIndex = 11;
-			this.label2.Text = "Warnings";
-			// 
-			// label1
-			// 
-			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(3, 5);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(58, 13);
-			this.label1.TabIndex = 10;
-			this.label1.Text = "Characters";
-			// 
 			// lstCharacters
 			// 
-			this.lstCharacters.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left)));
+			this.lstCharacters.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstCharacters.BackColor = System.Drawing.Color.White;
+			this.lstCharacters.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstCharacters.ForeColor = System.Drawing.Color.Black;
 			this.lstCharacters.FormattingEnabled = true;
-			this.lstCharacters.Location = new System.Drawing.Point(3, 21);
+			this.lstCharacters.Location = new System.Drawing.Point(6, 23);
 			this.lstCharacters.Name = "lstCharacters";
-			this.lstCharacters.Size = new System.Drawing.Size(215, 498);
+			this.lstCharacters.Size = new System.Drawing.Size(200, 433);
 			this.lstCharacters.TabIndex = 8;
 			this.lstCharacters.SelectedIndexChanged += new System.EventHandler(this.lstCharacters_SelectedIndexChanged);
 			// 
@@ -105,10 +91,13 @@
 			this.lstWarnings.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstWarnings.BackColor = System.Drawing.Color.White;
+			this.lstWarnings.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstWarnings.ForeColor = System.Drawing.Color.Black;
 			this.lstWarnings.HorizontalScrollbar = true;
-			this.lstWarnings.Location = new System.Drawing.Point(3, 0);
+			this.lstWarnings.Location = new System.Drawing.Point(0, 0);
 			this.lstWarnings.Name = "lstWarnings";
-			this.lstWarnings.Size = new System.Drawing.Size(843, 615);
+			this.lstWarnings.Size = new System.Drawing.Size(837, 563);
 			this.lstWarnings.TabIndex = 9;
 			this.lstWarnings.SelectedIndexChanged += new System.EventHandler(this.lstWarnings_SelectedIndexChanged);
 			this.lstWarnings.DoubleClick += new System.EventHandler(this.lstWarnings_DoubleClick);
@@ -119,20 +108,24 @@
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.pnlValid.Controls.Add(this.label4);
-			this.pnlValid.Location = new System.Drawing.Point(224, 21);
+			this.pnlValid.Location = new System.Drawing.Point(6, 25);
 			this.pnlValid.Name = "pnlValid";
-			this.pnlValid.Size = new System.Drawing.Size(849, 649);
+			this.pnlValid.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.pnlValid.Size = new System.Drawing.Size(837, 597);
 			this.pnlValid.TabIndex = 14;
+			this.pnlValid.TabSide = Desktop.Skinning.TabSide.None;
 			this.pnlValid.Visible = false;
 			// 
 			// label4
 			// 
 			this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
-			this.label4.Font = new System.Drawing.Font("Segoe UI", 40F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.label4.ForeColor = System.Drawing.Color.Green;
-			this.label4.Location = new System.Drawing.Point(3, 235);
+			this.label4.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label4.Location = new System.Drawing.Point(3, 209);
 			this.label4.Name = "label4";
-			this.label4.Size = new System.Drawing.Size(843, 173);
+			this.label4.Size = new System.Drawing.Size(831, 173);
 			this.label4.TabIndex = 0;
 			this.label4.Text = "Everything Checks Out!";
 			this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -142,39 +135,75 @@
 			this.pnlWarnings.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.pnlWarnings.Controls.Add(this.cmdCopyAll);
-			this.pnlWarnings.Controls.Add(this.cmdCopy);
-			this.pnlWarnings.Controls.Add(this.cmdGoTo);
+			this.pnlWarnings.Controls.Add(this.skinnedPanel1);
 			this.pnlWarnings.Controls.Add(this.lstWarnings);
-			this.pnlWarnings.Location = new System.Drawing.Point(224, 21);
+			this.pnlWarnings.Location = new System.Drawing.Point(6, 25);
 			this.pnlWarnings.Name = "pnlWarnings";
-			this.pnlWarnings.Size = new System.Drawing.Size(849, 649);
+			this.pnlWarnings.PanelType = Desktop.Skinning.SkinnedBackgroundType.Transparent;
+			this.pnlWarnings.Size = new System.Drawing.Size(837, 597);
 			this.pnlWarnings.TabIndex = 14;
+			this.pnlWarnings.TabSide = Desktop.Skinning.TabSide.None;
 			// 
-			// cmdCopy
+			// skinnedPanel1
 			// 
-			this.cmdCopy.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-			this.cmdCopy.Location = new System.Drawing.Point(3, 621);
-			this.cmdCopy.Name = "cmdCopy";
-			this.cmdCopy.Size = new System.Drawing.Size(115, 23);
-			this.cmdCopy.TabIndex = 11;
-			this.cmdCopy.Text = "Copy to Clipboard";
-			this.toolTip1.SetToolTip(this.cmdCopy, "Copies all validation warnings for this character to the clipboard");
-			this.cmdCopy.UseVisualStyleBackColor = true;
-			this.cmdCopy.Click += new System.EventHandler(this.cmdCopy_Click);
+			this.skinnedPanel1.Controls.Add(this.cmdGoTo);
+			this.skinnedPanel1.Controls.Add(this.cmdCopy);
+			this.skinnedPanel1.Controls.Add(this.cmdCopyAll);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 565);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedPanel1.Size = new System.Drawing.Size(837, 32);
+			this.skinnedPanel1.TabIndex = 13;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
 			// 
 			// cmdGoTo
 			// 
 			this.cmdGoTo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdGoTo.Location = new System.Drawing.Point(746, 621);
+			this.cmdGoTo.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdGoTo.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdGoTo.Flat = false;
+			this.cmdGoTo.Location = new System.Drawing.Point(681, 5);
 			this.cmdGoTo.Name = "cmdGoTo";
-			this.cmdGoTo.Size = new System.Drawing.Size(100, 23);
+			this.cmdGoTo.Size = new System.Drawing.Size(153, 23);
 			this.cmdGoTo.TabIndex = 10;
 			this.cmdGoTo.Text = "Go to Warning";
 			this.toolTip1.SetToolTip(this.cmdGoTo, "Jumps to the line causing the selected warning");
 			this.cmdGoTo.UseVisualStyleBackColor = true;
 			this.cmdGoTo.Click += new System.EventHandler(this.cmdGoTo_Click);
 			// 
+			// cmdCopy
+			// 
+			this.cmdCopy.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.cmdCopy.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCopy.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCopy.Flat = false;
+			this.cmdCopy.ForeColor = System.Drawing.Color.Blue;
+			this.cmdCopy.Location = new System.Drawing.Point(3, 5);
+			this.cmdCopy.Name = "cmdCopy";
+			this.cmdCopy.Size = new System.Drawing.Size(176, 23);
+			this.cmdCopy.TabIndex = 11;
+			this.cmdCopy.Text = "Copy to Clipboard";
+			this.toolTip1.SetToolTip(this.cmdCopy, "Copies all validation warnings for this character to the clipboard");
+			this.cmdCopy.UseVisualStyleBackColor = true;
+			this.cmdCopy.Click += new System.EventHandler(this.cmdCopy_Click);
+			// 
+			// cmdCopyAll
+			// 
+			this.cmdCopyAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.cmdCopyAll.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCopyAll.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCopyAll.Flat = false;
+			this.cmdCopyAll.ForeColor = System.Drawing.Color.Blue;
+			this.cmdCopyAll.Location = new System.Drawing.Point(183, 5);
+			this.cmdCopyAll.Name = "cmdCopyAll";
+			this.cmdCopyAll.Size = new System.Drawing.Size(176, 23);
+			this.cmdCopyAll.TabIndex = 12;
+			this.cmdCopyAll.Text = "Copy All to Clipboard";
+			this.toolTip1.SetToolTip(this.cmdCopyAll, "Copies all validation warnings for every character to the clipboard");
+			this.cmdCopyAll.UseVisualStyleBackColor = true;
+			this.cmdCopyAll.Click += new System.EventHandler(this.cmdCopyAll_Click);
+			// 
 			// pnlProgress
 			// 
 			this.pnlProgress.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
@@ -182,19 +211,24 @@
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.pnlProgress.Controls.Add(this.lblProgress);
 			this.pnlProgress.Controls.Add(this.progressBar);
-			this.pnlProgress.Location = new System.Drawing.Point(224, 21);
+			this.pnlProgress.Location = new System.Drawing.Point(6, 25);
 			this.pnlProgress.Name = "pnlProgress";
-			this.pnlProgress.Size = new System.Drawing.Size(849, 649);
+			this.pnlProgress.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.pnlProgress.Size = new System.Drawing.Size(837, 597);
 			this.pnlProgress.TabIndex = 1;
+			this.pnlProgress.TabSide = Desktop.Skinning.TabSide.None;
 			this.pnlProgress.Visible = false;
 			// 
 			// lblProgress
 			// 
 			this.lblProgress.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblProgress.Font = new System.Drawing.Font("Microsoft Sans Serif", 16F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.lblProgress.Location = new System.Drawing.Point(3, 286);
+			this.lblProgress.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblProgress.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblProgress.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblProgress.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblProgress.Location = new System.Drawing.Point(3, 260);
 			this.lblProgress.Name = "lblProgress";
-			this.lblProgress.Size = new System.Drawing.Size(843, 50);
+			this.lblProgress.Size = new System.Drawing.Size(831, 50);
 			this.lblProgress.TabIndex = 3;
 			this.lblProgress.Text = "Loading...";
 			this.lblProgress.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -202,62 +236,86 @@
 			// progressBar
 			// 
 			this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
-			this.progressBar.Location = new System.Drawing.Point(282, 339);
+			this.progressBar.Location = new System.Drawing.Point(282, 313);
 			this.progressBar.Name = "progressBar";
-			this.progressBar.Size = new System.Drawing.Size(276, 23);
+			this.progressBar.Size = new System.Drawing.Size(264, 23);
 			this.progressBar.TabIndex = 2;
 			// 
-			// cmdCopyAll
+			// skinnedGroupBox1
 			// 
-			this.cmdCopyAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-			this.cmdCopyAll.Location = new System.Drawing.Point(124, 621);
-			this.cmdCopyAll.Name = "cmdCopyAll";
-			this.cmdCopyAll.Size = new System.Drawing.Size(115, 23);
-			this.cmdCopyAll.TabIndex = 12;
-			this.cmdCopyAll.Text = "Copy All to Clipboard";
-			this.toolTip1.SetToolTip(this.cmdCopyAll, "Copies all validation warnings for every character to the clipboard");
-			this.cmdCopyAll.UseVisualStyleBackColor = true;
-			this.cmdCopyAll.Click += new System.EventHandler(this.cmdCopyAll_Click);
+			this.skinnedGroupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+			this.skinnedGroupBox1.Controls.Add(this.lstCharacters);
+			this.skinnedGroupBox1.Location = new System.Drawing.Point(6, 5);
+			this.skinnedGroupBox1.Name = "skinnedGroupBox1";
+			this.skinnedGroupBox1.Size = new System.Drawing.Size(212, 475);
+			this.skinnedGroupBox1.TabIndex = 13;
+			this.skinnedGroupBox1.TabStop = false;
+			this.skinnedGroupBox1.Text = "Characters";
+			// 
+			// skinnedGroupBox2
+			// 
+			this.skinnedGroupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.skinnedGroupBox2.Controls.Add(this.lstFilters);
+			this.skinnedGroupBox2.Location = new System.Drawing.Point(6, 486);
+			this.skinnedGroupBox2.Name = "skinnedGroupBox2";
+			this.skinnedGroupBox2.Size = new System.Drawing.Size(212, 147);
+			this.skinnedGroupBox2.TabIndex = 9;
+			this.skinnedGroupBox2.TabStop = false;
+			this.skinnedGroupBox2.Text = "Filters";
+			// 
+			// skinnedGroupBox3
+			// 
+			this.skinnedGroupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedGroupBox3.Controls.Add(this.pnlWarnings);
+			this.skinnedGroupBox3.Controls.Add(this.pnlValid);
+			this.skinnedGroupBox3.Controls.Add(this.pnlProgress);
+			this.skinnedGroupBox3.Location = new System.Drawing.Point(224, 5);
+			this.skinnedGroupBox3.Name = "skinnedGroupBox3";
+			this.skinnedGroupBox3.Size = new System.Drawing.Size(849, 628);
+			this.skinnedGroupBox3.TabIndex = 13;
+			this.skinnedGroupBox3.TabStop = false;
+			this.skinnedGroupBox3.Text = "Warnings";
 			// 
 			// ValidationControl
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.pnlWarnings);
-			this.Controls.Add(this.label3);
-			this.Controls.Add(this.lstFilters);
-			this.Controls.Add(this.label2);
-			this.Controls.Add(this.label1);
-			this.Controls.Add(this.lstCharacters);
-			this.Controls.Add(this.pnlProgress);
-			this.Controls.Add(this.pnlValid);
+			this.Controls.Add(this.skinnedGroupBox3);
+			this.Controls.Add(this.skinnedGroupBox2);
+			this.Controls.Add(this.skinnedGroupBox1);
 			this.Name = "ValidationControl";
-			this.Size = new System.Drawing.Size(1076, 673);
+			this.Size = new System.Drawing.Size(1076, 636);
 			this.pnlValid.ResumeLayout(false);
 			this.pnlWarnings.ResumeLayout(false);
+			this.skinnedPanel1.ResumeLayout(false);
 			this.pnlProgress.ResumeLayout(false);
+			this.skinnedGroupBox1.ResumeLayout(false);
+			this.skinnedGroupBox2.ResumeLayout(false);
+			this.skinnedGroupBox3.ResumeLayout(false);
 			this.ResumeLayout(false);
-			this.PerformLayout();
 
 		}
 
 		#endregion
-
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.ListBox lstFilters;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.ListBox lstCharacters;
-		private System.Windows.Forms.ListBox lstWarnings;
-		private System.Windows.Forms.Panel pnlWarnings;
-		private System.Windows.Forms.Panel pnlValid;
-		private System.Windows.Forms.Label label4;
-		private System.Windows.Forms.Panel pnlProgress;
-		private System.Windows.Forms.Label lblProgress;
-		private System.Windows.Forms.ProgressBar progressBar;
-		private System.Windows.Forms.Button cmdGoTo;
-		private System.Windows.Forms.Button cmdCopy;
+		private Desktop.Skinning.SkinnedListBox lstFilters;
+		private Desktop.Skinning.SkinnedListBox lstCharacters;
+		private Desktop.Skinning.SkinnedListBox lstWarnings;
+		private Desktop.Skinning.SkinnedPanel pnlWarnings;
+		private Desktop.Skinning.SkinnedPanel pnlValid;
+		private Desktop.Skinning.SkinnedLabel label4;
+		private Desktop.Skinning.SkinnedPanel pnlProgress;
+		private Desktop.Skinning.SkinnedLabel lblProgress;
+		private Desktop.Skinning.SkinnedProgressBar progressBar;
+		private Desktop.Skinning.SkinnedButton cmdGoTo;
+		private Desktop.Skinning.SkinnedButton cmdCopy;
 		private System.Windows.Forms.ToolTip toolTip1;
-		private System.Windows.Forms.Button cmdCopyAll;
+		private Desktop.Skinning.SkinnedButton cmdCopyAll;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox1;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox2;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox3;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Controls/ValidationControl.cs b/editor source/SPNATI Character Editor/Controls/ValidationControl.cs
index 694ed2ed652cb3487794dbce9e490213d64efbc2..b6d3740c31cad944aa7941cedab998cbba28929b 100644
--- a/editor source/SPNATI Character Editor/Controls/ValidationControl.cs	
+++ b/editor source/SPNATI Character Editor/Controls/ValidationControl.cs	
@@ -43,7 +43,7 @@ namespace SPNATI_Character_Editor.Controls
 		{
 			pnlValid.Visible = false;
 			List<ValidationError> warnings;
-			bool valid = CharacterValidator.Validate(character, Listing.Instance, out warnings);
+			bool valid = CharacterValidator.Validate(character, out warnings);
 			if (valid)
 			{
 				pnlValid.Visible = true;
@@ -123,7 +123,7 @@ namespace SPNATI_Character_Editor.Controls
 							continue; //don't validate characters that aren't in the main opponents folder, since they're likely to have errors but aren't being actively worked on
 						progress.Report(c);
 						List<ValidationError> warnings;
-						if (!CharacterValidator.Validate(c, Listing.Instance, out warnings))
+						if (!CharacterValidator.Validate(c, out warnings))
 						{
 							allWarnings[c] = warnings;
 						}
@@ -156,7 +156,7 @@ namespace SPNATI_Character_Editor.Controls
 				if (level == ValidationFilterLevel.None)
 					continue;
 				lstFilters.Items.Add(level);
-				if (level != ValidationFilterLevel.Minor)
+				if (level != ValidationFilterLevel.Minor && level != ValidationFilterLevel.MissingTargets)
 					lstFilters.SelectedItems.Add(level);
 			}
 		}
@@ -245,6 +245,9 @@ namespace SPNATI_Character_Editor.Controls
 				case ValidationContext.Area.Epilogue:
 					Shell.Instance.Launch<Character, EpilogueEditor>(_character, error.Context);
 					break;
+				case ValidationContext.Area.Collectible:
+					Shell.Instance.Launch<Character, CollectibleEditor>(_character, error.Context);
+					break;
 			}			
 		}
 
diff --git a/editor source/SPNATI Character Editor/DataSlicers/IntervalSlicer.cs b/editor source/SPNATI Character Editor/DataSlicers/IntervalSlicer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..691ad60e64761e682a664f2d331c5ebdc7e7a809
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataSlicers/IntervalSlicer.cs	
@@ -0,0 +1,135 @@
+using Desktop;
+using Desktop.CommonControls;
+using Desktop.Reporting;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace SPNATI_Character_Editor.DataSlicers
+{
+	public class IntervalSlicer : BaseSlicer<Range>
+	{
+		public string Property;
+
+		public ISlicerGroup NullGroup { get; private set; }
+
+		public int Min;
+		public int Max;
+
+		public IntervalSlicer(string property, string displayName, int min, int max)
+		{
+			Property = property;
+			DisplayName = displayName;
+			Min = min;
+			Max = max;
+
+			NullGroup = AddGroup(-1);
+			NullGroup.Key = "-";
+			NullGroup.Label = "None";
+			NullGroup.Groupable = false;
+
+			ISlicerGroup grp1 = AddGroup(new Range(min, max));
+			grp1.Groupable = false;
+		}
+
+		public override List<DataBucket> Slice(IEnumerable<ISliceable> dataSet)
+		{
+			Dictionary<int, DataBucket> buckets = new Dictionary<int, DataBucket>();
+			List<DataBucket> list = new List<DataBucket>();
+			foreach (SlicerGroup<Range> group in Groups)
+			{
+				if (!group.Active || group.Key == "-") { continue; }
+				DataBucket bucket = new DataBucket(group.Label);
+				foreach (Range value in group.Values)
+				{
+					for (int i = value.Min; i <= value.Max; i++)
+					{
+						buckets[i] = bucket;
+					}
+				}
+				list.Add(bucket);
+			}
+			list.Sort();
+			DataBucket nullBucket = null;
+			if (NullGroup.Active)
+			{
+				nullBucket = new DataBucket(NullGroup.Label);
+				list.Add(nullBucket);
+			}
+
+			foreach (ISliceable obj in dataSet)
+			{
+				MemberInfo mi = PropertyTypeInfo.GetMemberInfo(obj.GetType(), Property);
+				string value = mi.GetValue(obj)?.ToString() ?? "";
+				if (!string.IsNullOrEmpty(value))
+				{
+					int min, max;
+					string minStr, maxStr;
+					string[] pieces = value.Split('-');
+					minStr = pieces[0];
+					if (pieces.Length > 1)
+					{
+						maxStr = pieces[1];
+					}
+					else
+					{
+						maxStr = minStr;
+					}
+					if (!int.TryParse(minStr, out min))
+					{
+						min = 0;
+					}
+					if (!int.TryParse(maxStr, out max))
+					{
+						max = int.MaxValue;
+					}
+
+					HashSet<DataBucket> usedBuckets = new HashSet<DataBucket>();
+					foreach (int bucketKey in buckets.Keys)
+					{
+						if (bucketKey >= min && bucketKey <= max)
+						{
+							DataBucket bucket = buckets[bucketKey];
+							if (!usedBuckets.Contains(bucket))
+							{
+								bucket.Count += obj.GetSliceCount();
+								bucket.Data.Add(obj);
+								usedBuckets.Add(bucket);
+							}
+						}
+					}
+				}
+				else if (nullBucket != null)
+				{
+					nullBucket.Count += obj.GetSliceCount();
+					nullBucket.Data.Add(obj);
+				}
+			}
+
+			return list;
+		}
+	}
+
+	public class Range
+	{
+		public int Min;
+		public int Max;
+
+		public Range(int min, int max)
+		{
+			Min = min;
+			Max = max;
+		}
+
+		public Range Split(int pt)
+		{
+			int oldMax = Max;
+			Max = pt - 1;
+			return new Range(pt, oldMax);
+		}
+
+		public override string ToString()
+		{
+			return $"{Min}-{Max}";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataSlicers/OneShotSlicer.cs b/editor source/SPNATI Character Editor/DataSlicers/OneShotSlicer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..268bc131506985863895e67220e0b9b391dfa7bf
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataSlicers/OneShotSlicer.cs	
@@ -0,0 +1,52 @@
+using Desktop;
+using Desktop.CommonControls;
+using Desktop.Reporting;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace SPNATI_Character_Editor.DataSlicers
+{
+	public class OneShotSlicer : BaseSlicer<int>
+	{
+		public ISlicerGroup YesGroup { get; private set; }
+		public ISlicerGroup NoGroup { get; private set; }
+
+		public OneShotSlicer() : base()
+		{
+			DisplayName = "Play Once";
+			NoGroup = AddGroup(0);
+			NoGroup.Label = "No";
+			YesGroup = AddGroup(1);
+			YesGroup.Label = "Yes";
+		}
+
+		public override List<DataBucket> Slice(IEnumerable<ISliceable> dataSet)
+		{
+			DataBucket yesBucket = new DataBucket(YesGroup.Label);
+			DataBucket noBucket = new DataBucket(NoGroup.Label);
+			foreach (ISliceable obj in dataSet)
+			{
+				MemberInfo mi = PropertyTypeInfo.GetMemberInfo(obj.GetType(), "OneShotId");
+				int value = (int)mi.GetValue(obj);
+				DataBucket bucket = noBucket;
+				if (value > 0)
+				{
+					bucket = yesBucket;
+				}
+				bucket.Count += obj.GetSliceCount();
+				bucket.Data.Add(obj);
+			}
+
+			List<DataBucket> list = new List<DataBucket>();
+			if (YesGroup.Active)
+			{
+				list.Add(yesBucket);
+			}
+			if (NoGroup.Active)
+			{
+				list.Add(noBucket);
+			}
+			return list;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataSlicers/StageSlicer.cs b/editor source/SPNATI Character Editor/DataSlicers/StageSlicer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..29dc899285b7ca7f4fd41578933a37c9685e85d7
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataSlicers/StageSlicer.cs	
@@ -0,0 +1,65 @@
+using Desktop.Reporting;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SPNATI_Character_Editor.DataSlicers
+{
+	public class StageSlicer : BaseSlicer<int>
+	{
+		public StageSlicer() : base()
+		{
+			DisplayName = "Case Stage";
+		}
+
+		protected override void OnPropertyChanged(string propName)
+		{
+			if (propName == "Context")
+			{
+				Character character = Context as Character;
+				ClearGroups();
+				for (int i = 0; i < character.Layers + Clothing.ExtraStages; i++)
+				{
+					StageName stage = character.LayerToStageName(i);
+					ISlicerGroup group = AddGroup(i);
+					group.Label = stage.DisplayName;
+				}
+				
+			}
+		}
+
+		public override List<DataBucket> Slice(IEnumerable<ISliceable> dataSet)
+		{
+			Dictionary<int, DataBucket> buckets = new Dictionary<int, DataBucket>();
+			List<DataBucket> list = new List<DataBucket>();
+			foreach (ISlicerGroup group in Groups)
+			{
+				if (!group.Active) { continue; }
+				DataBucket bucket = new DataBucket(group.Label);
+				foreach (int value in group.Values)
+				{
+					buckets[value] = bucket;
+				}
+				list.Add(bucket);
+			}
+
+			foreach (ISliceable obj in dataSet)
+			{
+				Case c = obj as Case;
+				foreach (int stage in c.Stages)
+				{
+					DataBucket stageBucket;
+					if (buckets.TryGetValue(stage, out stageBucket))
+					{
+						stageBucket.Count += obj.GetSliceCount();
+						stageBucket.Data.Add(obj);
+					}
+				}
+			}
+
+			return list;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataStructures/Behaviour.cs b/editor source/SPNATI Character Editor/DataStructures/Behaviour.cs
index ee2cfac9970e619c68578aba6ad38f50e3cb5f06..0376b72ecabb5bee267941c18a3c7dce27223bab 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Behaviour.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Behaviour.cs	
@@ -4,6 +4,7 @@ using System.IO;
 using System.Xml.Serialization;
 using System.Linq;
 using SPNATI_Character_Editor.IO;
+using System.Collections.ObjectModel;
 
 namespace SPNATI_Character_Editor
 {
@@ -32,6 +33,12 @@ namespace SPNATI_Character_Editor
 		/// </summary>
 		[XmlIgnore]
 		public int NextId { get; set; }
+		[XmlIgnore]
+		public int MaxCaseId { get; set; }
+		[XmlIgnore]
+		public int MaxStageId { get; set; }
+
+		private bool _temporary;
 
 		/// <summary>
 		/// Only used when serializing or deserializing XML. Cases that share text across stages are split into separate cases per stage here
@@ -88,6 +95,25 @@ namespace SPNATI_Character_Editor
 			}
 		}
 
+		/// <summary>
+		/// The Banter Wizard has to build WorkingCases for every character, which runs into OutOfMemoryExceptions.
+		/// This is a quick and dirty measure to avoid that by unloading the working cases after we no longer need them, but only if we aren't actually editing this character
+		/// </summary>
+		public void FlagTemporary()
+		{
+			if (_builtWorkingCases) { return; }
+			_temporary = true;
+		}
+		public void ReleaseTemporary()
+		{
+			if (_temporary)
+			{
+				_temporary = false;
+				_builtWorkingCases = false;
+				_workingCases.Clear();
+			}
+		}
+
 		/// <summary>
 		/// Called when loading a character to edit
 		/// </summary>
@@ -115,6 +141,12 @@ namespace SPNATI_Character_Editor
 		public static DialogueLine CreateStageSpecificLine(DialogueLine line, int stage, Character character)
 		{
 			DialogueLine copy = line.Copy();
+			if (copy.StageImages.ContainsKey(stage))
+			{
+				LineImage li = copy.StageImages[stage];
+				copy.Image = li.Image;
+				copy.IsGenericImage = li.IsGenericImage;
+			}
 			if (!copy.IsGenericImage)
 			{
 				copy.Image = DialogueLine.GetStageImage(stage, copy.Image);
@@ -122,50 +154,51 @@ namespace SPNATI_Character_Editor
 			if (copy.Image != null)
 			{
 				bool custom = copy.Image.StartsWith("custom:");
-
 				string path = character != null ? Config.GetRootDirectory(character) : "";
 				string extension = line.ImageExtension;
-				if (string.IsNullOrEmpty(extension) && !custom)
+				if (!custom)
 				{
-					//figure out the extension by searching for files of different names
-					bool basePngExists = File.Exists(Path.Combine(path, copy.Image + ".png"));
-					bool baseGifExists = File.Exists(Path.Combine(path, copy.Image + ".gif"));
-
-					if (!copy.Image.StartsWith(stage + "-") && !basePngExists && !baseGifExists)
+					if (string.IsNullOrEmpty(extension))
 					{
-						copy.Image = stage + "-" + copy.Image;
-						baseGifExists = File.Exists(Path.Combine(path, copy.Image + ".gif"));
-						if (baseGifExists)
+						//figure out the extension by searching for files of different names
+						bool basePngExists = File.Exists(Path.Combine(path, copy.Image + ".png"));
+						bool baseGifExists = File.Exists(Path.Combine(path, copy.Image + ".gif"));
+
+						if (!copy.Image.StartsWith(stage + "-") && !basePngExists && !baseGifExists)
 						{
-							extension = ".gif";
+							copy.Image = stage + "-" + copy.Image;
+							baseGifExists = File.Exists(Path.Combine(path, copy.Image + ".gif"));
+							if (baseGifExists)
+							{
+								extension = ".gif";
+							}
+							else
+							{
+								extension = ".png";
+							}
 						}
 						else
 						{
-							extension = ".png";
+							if (baseGifExists)
+							{
+								extension = ".gif";
+							}
+							else
+							{
+								extension = ".png";
+							}
 						}
 					}
-					else
+					if (!copy.Image.StartsWith(stage + "-") && !File.Exists(Path.Combine(path, copy.Image + extension)))
 					{
-						if (baseGifExists)
-						{
-							extension = ".gif";
-						}
-						else
-						{
-							extension = ".png";
-						}
+						copy.Image = stage + "-" + copy.Image;
 					}
+					if (extension != null && !copy.Image.EndsWith(extension))
+					{
+						copy.Image += extension;
+					}
+					copy.ImageExtension = extension;
 				}
-
-				if (!custom && !copy.Image.StartsWith(stage + "-") && !File.Exists(Path.Combine(path, copy.Image + extension)))
-				{
-					copy.Image = stage + "-" + copy.Image;
-				}
-				if (extension != null && !copy.Image.EndsWith(extension))
-				{
-					copy.Image += extension;
-				}
-				copy.ImageExtension = extension;
 			}
 			copy.Text = line.Text?.Trim();
 			return copy;
@@ -274,12 +307,8 @@ namespace SPNATI_Character_Editor
 		public static DialogueLine CreateDefaultLine(DialogueLine line)
 		{
 			DialogueLine copy = line.Copy();
-			string extension = line.ImageExtension ?? Path.GetExtension(line.Image);
-			copy.ImageExtension = extension;
-			line.ImageExtension = extension;
-			copy.Image = DialogueLine.GetDefaultImage(line.Image);
 			copy.Text = line.Text.Trim();
-			copy.IsGenericImage = line.IsGenericImage;
+			copy.GeneralizeImage(line);
 			return copy;
 		}
 
@@ -328,8 +357,16 @@ namespace SPNATI_Character_Editor
 			character.Metadata.CrossGender = false;
 
 			//Put each case into the appropriate stage(s)
-			foreach (var workingCase in _workingCases)
+			foreach (Case workingCase in _workingCases)
 			{
+				List<Case> alternativeCases = new List<Case>();
+				alternativeCases.Add(workingCase);
+				foreach (Case alternate in workingCase.AlternativeConditions)
+				{
+					//copy lines and stages into the alternate cases
+					alternativeCases.Add(alternate);
+				}
+
 				foreach (int s in workingCase.Stages)
 				{
 					if (s >= Stages.Count) { continue; }
@@ -343,24 +380,28 @@ namespace SPNATI_Character_Editor
 					Stage stage = Stages[s];
 
 					//Find a case to merge into
-					Case existingCase = stage.Cases.Find(c => c.MatchesConditions(workingCase) && (c.StageId == id || (string.IsNullOrEmpty(id) && string.IsNullOrEmpty(c.StageId))));
-					if (existingCase == null)
-					{
-						//No case exists yet, so create one
-						existingCase = workingCase.CopyConditions();
-						existingCase.StageId = id;
-						existingCase.Stages.Add(s); //Not really necessary for serialization, since each case will have a single stage, and will be a child of that stage
-						stage.Cases.Add(existingCase);
-					}
-
-					//Move the lines over, and make them stage-specific
-					foreach (var line in workingCase.Lines)
+					foreach (Case sourceCase in alternativeCases)
 					{
-						existingCase.Lines.Add(CreateStageSpecificLine(line, s, character));
+						Case existingCase = stage.Cases.Find(c => c.MatchesConditions(sourceCase) && (c.StageId == id || (string.IsNullOrEmpty(id) && string.IsNullOrEmpty(c.StageId))));
+						if (existingCase == null)
+						{
+							//No case exists yet, so create one
+							existingCase = sourceCase.CopyConditions();
+							existingCase.StageId = id;
+							existingCase.Stages.Add(s); //Not really necessary for serialization, since each case will have a single stage, and will be a child of that stage
+							stage.Cases.Add(existingCase);
+						}
 
-						if (!string.IsNullOrEmpty(line.Gender))
+						//Move the lines over, and make them stage-specific
+						foreach (var line in workingCase.Lines)
 						{
-							character.Metadata.CrossGender = true;
+							DialogueLine stageLine = CreateStageSpecificLine(line, s, character);
+							existingCase.Lines.Add(stageLine);
+
+							if (!string.IsNullOrEmpty(line.Gender))
+							{
+								character.Metadata.CrossGender = true;
+							}
 						}
 					}
 				}
@@ -386,6 +427,11 @@ namespace SPNATI_Character_Editor
 						continue;
 					int code = stageCase.GetCode();
 
+					if (stageCase.OneShotId > 0)
+					{
+						MaxCaseId = Math.Max(MaxCaseId, stageCase.OneShotId);
+					}
+
 					int id = 0;
 					if (!string.IsNullOrEmpty(stageCase.StageId))
 					{
@@ -396,12 +442,20 @@ namespace SPNATI_Character_Editor
 						}
 					}
 
+					HashSet<string> builtCases = new HashSet<string>();
 					foreach (DialogueLine line in stageCase.Lines)
 					{
+						bool addedDuplicate = false;
 						line.IsGenericImage = !DialogueLine.IsStageSpecificImage(line.Image);
 						var defaultLine = CreateDefaultLine(line);
-						int hash = defaultLine.GetHashCode();
-						hash = code + hash;
+
+						if (defaultLine.OneShotId > 0)
+						{
+							MaxStageId = Math.Max(MaxStageId, defaultLine.OneShotId);
+						}
+
+						int lineHash = defaultLine.GetHashCodeWithoutImage();
+						int hash = (code * 397) ^ lineHash;
 						//See if there's a case that already contains this line, and make one if there isn't
 						Case existing;
 						if (!map.TryGetValue(hash, out existing))
@@ -411,16 +465,42 @@ namespace SPNATI_Character_Editor
 							map[hash] = existing;
 							existing.Lines.Add(defaultLine);
 							buckets.Add(existing);
+							if (!string.IsNullOrEmpty(defaultLine.Text))
+							{
+								builtCases.Add(defaultLine.Text);
+							}
+						}
+						else if (builtCases.Contains(defaultLine.Text))
+						{
+							//If the same text appears multiple times in the same case, make sure to include them all
+							existing.Lines.Add(defaultLine);
 						}
 						if (!existing.Stages.Contains(stage.Id))
 						{
 							existing.Stages.Add(stage.Id);
+
+							if (!addedDuplicate)
+							{
+								//find the existing line if there is one
+								foreach (DialogueLine existingLine in existing.Lines)
+								{
+									if (existingLine.GetHashCodeWithoutImage() == lineHash)
+									{
+										if (existingLine.Image != defaultLine.Image)
+										{
+											//if the images are different, remember that difference
+											existingLine.StageImages[stage.Id] = new LineImage(defaultLine.Image, line.IsGenericImage);
+										}
+										break;
+									}
+								}
+							}
 						}
 					}
 				}
 			}
 
-			//Sort each buckets's Stages set for easier equivalence checks
+			//Sort each bucket's Stages set for easier equivalence checks
 			foreach (Case c in buckets)
 			{
 				c.Stages.Sort();
@@ -445,16 +525,51 @@ namespace SPNATI_Character_Editor
 					caseMatchingStages.Stages.AddRange(bucket.Stages);
 					caseList.Add(caseMatchingStages);
 				}
-				foreach (var line in bucket.Lines)
+				foreach (DialogueLine line in bucket.Lines)
 				{
 					caseMatchingStages.Lines.Add(line);
 				}
 			}
 
+			Dictionary<int, List<Case>> lineCodes = new Dictionary<int, List<Case>>();
+
 			//Done grouping. Put the cases into the WorkingCase list
 			foreach (List<Case> list in cases.Values)
 			{
-				_workingCases.AddRange(list);
+				foreach (Case c in list)
+				{
+					int lineCode = c.GetLineCode();
+					List<Case> similarCases;
+					bool newCase = true;
+					if (lineCodes.TryGetValue(lineCode, out similarCases))
+					{
+						bool foundSimilar = false;
+						foreach (Case similar in similarCases)
+						{
+							if (similar.MatchesNonConditions(c))
+							{
+								similar.AlternativeConditions.Add(c);
+								foundSimilar = true;
+								newCase = false;
+								break;
+							}
+						}
+						if (!foundSimilar)
+						{
+							similarCases.Add(c);
+						}
+					}
+					else
+					{
+						similarCases = new List<Case>();
+						lineCodes[lineCode] = similarCases;
+						similarCases.Add(c);
+					}
+					if (newCase)
+					{
+						_workingCases.Add(c);
+					}
+				}
 			}
 
 			//Move the legacy Start lines into Selected/Game start cases
@@ -560,7 +675,7 @@ namespace SPNATI_Character_Editor
 			{
 				if (stage != retainStage)
 				{
-					Case stageCase = original.Copy();
+					Case stageCase = DuplicateCase(original, false);
 					stageCase.Stages.Add(stage);
 					AddWorkingCase(stageCase);
 				}
@@ -577,7 +692,7 @@ namespace SPNATI_Character_Editor
 		/// <param name="splitPoint">Stage to split at</param>
 		public void SplitCaseStage(Case original, int splitPoint)
 		{
-			Case beforeSplitCase = original.Copy();
+			Case beforeSplitCase = DuplicateCase(original, false);
 			for (int s = original.Stages.Count - 1; s >= 0; s--)
 			{
 				if (original.Stages[s] != splitPoint)
@@ -598,7 +713,11 @@ namespace SPNATI_Character_Editor
 		/// <param name="splitPoint">Stage to split at</param>
 		public void SplitCaseAtStage(Case original, int splitPoint)
 		{
-			Case beforeSplitCase = original.Copy();
+			if (original.Stages.Count == 0 || original.Stages[0] == splitPoint)
+			{
+				return;
+			}
+			Case beforeSplitCase = DuplicateCase(original, false);
 			for (int s = original.Stages.Count - 1; s >= 0; s--)
 			{
 				if (original.Stages[s] < splitPoint)
@@ -617,11 +736,30 @@ namespace SPNATI_Character_Editor
 		/// </summary>
 		/// <param name="original"></param>
 		/// <returns></returns>
-		public Case DuplicateCase(Case original)
+		public Case DuplicateCase(Case original, bool addToWorking)
 		{
 			Case copy = original.Copy();
-			copy.Stages.AddRange(original.Stages);
-			AddWorkingCase(copy);
+			if (copy.Id != 0)
+			{
+				CharacterEditorData editorData = CharacterDatabase.GetEditorData(_character);
+				editorData.Copy(copy);
+			}
+			if (copy.OneShotId > 0)
+			{
+				copy.OneShotId = ++MaxCaseId;
+			}
+			foreach (DialogueLine line in copy.Lines)
+			{
+				if (line.OneShotId > 0)
+				{
+					line.OneShotId = ++MaxStageId;
+				}
+			}
+			if (addToWorking)
+			{
+				copy.Stages.AddRange(original.Stages);
+				AddWorkingCase(copy);
+			}
 			return copy;
 		}
 
@@ -697,7 +835,7 @@ namespace SPNATI_Character_Editor
 		{
 			foreach (Case workingCase in _workingCases)
 			{
-				List<int> stages = workingCase.Stages;
+				ObservableCollection<int> stages = workingCase.Stages;
 				for (int i = 0; i < stages.Count; i++)
 				{
 					int stage = stages[i];
@@ -715,7 +853,7 @@ namespace SPNATI_Character_Editor
 		{
 			foreach (Case workingCase in _workingCases)
 			{
-				List<int> stages = workingCase.Stages;
+				ObservableCollection<int> stages = workingCase.Stages;
 				for (int i = stages.Count - 1; i >= 0; i--)
 				{
 					int stage = stages[i];
@@ -736,7 +874,7 @@ namespace SPNATI_Character_Editor
 		{
 			foreach (Case workingCase in _workingCases)
 			{
-				List<int> stages = workingCase.Stages;
+				ObservableCollection<int> stages = workingCase.Stages;
 				for (int i = 0; i < stages.Count; i++)
 				{
 					int stage = stages[i];
diff --git a/editor source/SPNATI Character Editor/DataStructures/Case.cs b/editor source/SPNATI Character Editor/DataStructures/Case.cs
index 376f47adfc3495613586e0fce5656dce05e7da8a..cbf2d1f51d54edf39aa74426da500eb7c69a41e2 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Case.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Case.cs	
@@ -1,28 +1,41 @@
 using Desktop;
+using Desktop.CommonControls;
 using Desktop.CommonControls.PropertyControls;
+using Desktop.DataStructures;
+using Desktop.Reporting;
+using Newtonsoft.Json;
+using SPNATI_Character_Editor.IO;
 using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Linq;
 using System.Reflection;
+using System.Runtime.Serialization;
 using System.Xml.Serialization;
 
 namespace SPNATI_Character_Editor
 {
-	/// <remarks>
-	/// For simplicity in serialization, fields are listed in the order that make_xml.py generates them, so that the two methods for generating xml files produce mostly equivalent output
-	/// </remarks>
-	public class Case : IComparable<Case>
+	[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+	public class Case : BindableObject, IComparable<Case>, ISliceable
 	{
 		private static long s_globalId;
 
+		private string _tag;
+		[XmlOrder(0)]
 		[XmlAttribute("tag")]
-		public string Tag;
+		[JsonProperty("tag")]
+		public string Tag
+		{
+			get { return _tag; }
+			set { if (_tag != value) { _tag = value; NotifyPropertyChanged(nameof(Tag)); } }
+		}
 
 		/// <summary>
 		/// Unique case identifier in the format Stage-Id. Unused by the game, but important for the editor
 		/// </summary>
 		[DefaultValue(null)]
+		[XmlOrder(1)]
 		[XmlAttribute("id")]
 		public string StageId;
 
@@ -32,153 +45,434 @@ namespace SPNATI_Character_Editor
 		[XmlIgnore]
 		public int Id;
 
+		/// <summary>
+		/// Only used with ImportEdits
+		/// </summary>
+		[JsonProperty("stage")]
+		[XmlIgnore]
+		public string Stage;
+
+		private int _oneShotId;
+		/// <summary>
+		/// Case will only play once
+		/// </summary>
+		[OneShot(OneShotMode.Case, DisplayName = "Play Once", GroupName = "Self", GroupOrder = 98, Description = "This call will only play once per game.")]
+		[DefaultValue(0)]
+		[XmlOrder(10)]
+		[JsonProperty("oneShotId")]
+		[XmlAttribute("oneShotId")]
+		public int OneShotId
+		{
+			get { return _oneShotId; }
+			set { if (_oneShotId != value) { _oneShotId = value; NotifyPropertyChanged(nameof(OneShotId)); } }
+		}
+
+		private string _target;
 		[RecordSelect(DisplayName = "Target", GroupName = "Target", GroupOrder = 0, Description = "Character performing the action", RecordType = typeof(Character), RecordFilter = "FilterTargetByCase", AllowCreate = true)]
+		[XmlOrder(20)]
 		[XmlAttribute("target")]
-		public string Target;
+		[JsonProperty("target")]
+		public string Target
+		{
+			get { return _target; }
+			set { if (_target != value) { _target = value; NotifyPropertyChanged(nameof(Target)); } }
+		}
 
+		private string _filter;
 		[RecordSelect(DisplayName = "Target Tag", GroupName = "Target", GroupOrder = 1, Description = "Target has a certain tag", RecordType = typeof(Tag), AllowCreate = true)]
+		[XmlOrder(30)]
 		[XmlAttribute("filter")]
-		public string Filter;
+		[JsonProperty("filter")]
+		public string Filter
+		{
+			get { return _filter; }
+			set { if (_filter != value) { _filter = value; NotifyPropertyChanged(nameof(Filter)); } }
+		}
 
+		private string _hidden;
 		[Boolean(DisplayName = "Hidden", GroupName = "Self", GroupOrder = 99, Description = "This case will evaluate and set markers when conditions are met, but the lines will not actually be displayed on screen", AutoCheck = true)]
+		[XmlOrder(40)]
 		[XmlAttribute("hidden")]
-		public string Hidden;
+		[JsonProperty("hidden")]
+		public string Hidden
+		{
+			get { return _hidden; }
+			set { if (_hidden != value) { _hidden = value; NotifyPropertyChanged(nameof(Hidden)); } }
+		}
 
+		private string _targetStage;
 		[StageSelect(DisplayName = "Target Stage", GroupName = "Target", GroupOrder = 2, Description = "Target is currently within a range of stages", BoundProperties = new string[] { "Target" }, FilterStagesToTarget = true, SkinVariable = "~target.costume~")]
+		[XmlOrder(50)]
 		[XmlAttribute("targetStage")]
-		public string TargetStage;
+		[JsonProperty("targetStage")]
+		public string TargetStage
+		{
+			get { return _targetStage; }
+			set { if (_targetStage != value) { _targetStage = value; NotifyPropertyChanged(nameof(TargetStage)); } }
+		}
 
+		private string _targetLayers;
 		[NumericRange(DisplayName = "Target Layers", GroupName = "Target", GroupOrder = 9, Description = "Number of layers the target has left")]
+		[XmlOrder(60)]
 		[XmlAttribute("targetLayers")]
-		public string TargetLayers;
+		[JsonProperty("targetLayers")]
+		public string TargetLayers
+		{
+			get { return _targetLayers; }
+			set { if (_targetLayers != value) { _targetLayers = value; NotifyPropertyChanged(nameof(TargetLayers)); } }
+		}
 
+		private string _targetStartingLayers;
 		[NumericRange(DisplayName = "Target Starting Layers", GroupName = "Target", GroupOrder = 10, Description = "Number of layers the target started with")]
+		[XmlOrder(70)]
 		[XmlAttribute("targetStartingLayers")]
-		public string TargetStartingLayers;
+		[JsonProperty("targetStartingLayers")]
+		public string TargetStartingLayers
+		{
+			get { return _targetStartingLayers; }
+			set { if (_targetStartingLayers != value) { _targetStartingLayers = value; NotifyPropertyChanged(nameof(TargetStartingLayers)); } }
+		}
 
+		private string _targetStatus;
 		[Status(DisplayName = "Target Status", GroupName = "Target", GroupOrder = 8, Description = "Target's current clothing status")]
+		[XmlOrder(80)]
 		[XmlAttribute("targetStatus")]
-		public string TargetStatus;
+		[JsonProperty("targetStatus")]
+		public string TargetStatus
+		{
+			get { return _targetStatus; }
+			set { if (_targetStatus != value) { _targetStatus = value; NotifyPropertyChanged(nameof(TargetStatus)); } }
+		}
 
+		private string _alsoPlaying;
 		[RecordSelect(DisplayName = "Also Playing", GroupName = "Also Playing", GroupOrder = 0, Description = "Character that is playing but not performing the current action", RecordType = typeof(Character), AllowCreate = true)]
+		[XmlOrder(90)]
 		[XmlAttribute("alsoPlaying")]
-		public string AlsoPlaying;
+		[JsonProperty("alsoPlaying")]
+		public string AlsoPlaying
+		{
+			get { return _alsoPlaying; }
+			set { if (_alsoPlaying != value) { _alsoPlaying = value; NotifyPropertyChanged(nameof(AlsoPlaying)); } }
+		}
 
+		private string _alsoPlayingStage;
 		[StageSelect(DisplayName = "Also Playing Stage", GroupName = "Also Playing", GroupOrder = 1, Description = "Character in Also Playing is currently within a range of stages", BoundProperties = new string[] { "AlsoPlaying" }, FilterStagesToTarget = false, SkinVariable = "~_.costume~")]
+		[XmlOrder(100)]
 		[XmlAttribute("alsoPlayingStage")]
-		public string AlsoPlayingStage;
+		[JsonProperty("alsoPlayingStage")]
+		public string AlsoPlayingStage
+		{
+			get { return _alsoPlayingStage; }
+			set { if (_alsoPlayingStage != value) { _alsoPlayingStage = value; NotifyPropertyChanged(nameof(AlsoPlayingStage)); } }
+		}
 
+		private string _alsoPlayingHand;
 		[ComboBox(DisplayName = "Also Playing Hand", GroupName = "Also Playing", GroupOrder = 6, Description = "Character in Also Playing has a particular poker hand",
 			Options = new string[] { "Nothing", "High Card", "One Pair", "Two Pair", "Three of a Kind", "Straight", "Flush", "Full House", "Four of a Kind", "Straight Flush", "Royal Flush" })]
+		[XmlOrder(110)]
 		[XmlAttribute("alsoPlayingHand")]
-		public string AlsoPlayingHand;
+		[JsonProperty("alsoPlayingHand")]
+		public string AlsoPlayingHand
+		{
+			get { return _alsoPlayingHand; }
+			set { if (_alsoPlayingHand != value) { _alsoPlayingHand = value; NotifyPropertyChanged(nameof(AlsoPlayingHand)); } }
+		}
 
+		private string _targetHand;
 		[ComboBox(DisplayName = "Target Hand", GroupName = "Target", GroupOrder = 7, Description = "Target has a particular poker hand",
 			Options = new string[] { "Nothing", "High Card", "One Pair", "Two Pair", "Three of a Kind", "Straight", "Flush", "Full House", "Four of a Kind", "Straight Flush", "Royal Flush" })]
+		[XmlOrder(120)]
 		[XmlAttribute("oppHand")]
-		public string TargetHand;
+		[JsonProperty("oppHand")]
+		public string TargetHand
+		{
+			get { return _targetHand; }
+			set { if (_targetHand != value) { _targetHand = value; NotifyPropertyChanged(nameof(TargetHand)); } }
+		}
 
+		private string _hasHand;
 		[ComboBox(DisplayName = "Has Hand", GroupName = "Self", GroupOrder = 5, Description = "Character has a particular poker hand",
 			Options = new string[] { "Nothing", "High Card", "One Pair", "Two Pair", "Three of a Kind", "Straight", "Flush", "Full House", "Four of a Kind", "Straight Flush", "Royal Flush" })]
+		[XmlOrder(130)]
 		[XmlAttribute("hasHand")]
-		public string HasHand;
+		[JsonProperty("hasHand")]
+		public string HasHand
+		{
+			get { return _hasHand; }
+			set { if (_hasHand != value) { _hasHand = value; NotifyPropertyChanged(nameof(HasHand)); } }
+		}
 
+		private string _totalMales;
 		[NumericRange(DisplayName = "Total Males", GroupName = "Table", GroupOrder = 2, Description = "Number of males playing (including this character and the player)", Minimum = 0, Maximum = 5)]
+		[XmlOrder(140)]
 		[XmlAttribute("totalMales")]
-		public string TotalMales;
+		[JsonProperty("totalMales")]
+		public string TotalMales
+		{
+			get { return _totalMales; }
+			set { if (_totalMales != value) { _totalMales = value; NotifyPropertyChanged(nameof(TotalMales)); } }
+		}
 
+		private string _totalFemales;
 		[NumericRange(DisplayName = "Total Females", GroupName = "Table", GroupOrder = 1, Description = "Number of females playing (including this character and the player)", Minimum = 0, Maximum = 5)]
+		[XmlOrder(150)]
 		[XmlAttribute("totalFemales")]
-		public string TotalFemales;
+		[JsonProperty("totalFemales")]
+		public string TotalFemales
+		{
+			get { return _totalFemales; }
+			set { if (_totalFemales != value) { _totalFemales = value; NotifyPropertyChanged(nameof(TotalFemales)); } }
+		}
 
+		private string _targetTimeInStage;
 		[NumericRange(DisplayName = "Target Time in Stage", GroupName = "Target", GroupOrder = 6, Description = "Number of rounds since the last time the target lost a hand")]
+		[XmlOrder(160)]
 		[XmlAttribute("targetTimeInStage")]
-		public string TargetTimeInStage;
+		[JsonProperty("targetTimeInStage")]
+		public string TargetTimeInStage
+		{
+			get { return _targetTimeInStage; }
+			set { if (_targetTimeInStage != value) { _targetTimeInStage = value; NotifyPropertyChanged(nameof(TargetTimeInStage)); } }
+		}
 
+		private string _alsoPlayingTimeInStage;
 		[NumericRange(DisplayName = "Also Playing Time in Stage", GroupName = "Also Playing", GroupOrder = 5, Description = "Number of rounds since the last time the Also Playing player lost a hand")]
+		[XmlOrder(170)]
 		[XmlAttribute("alsoPlayingTimeInStage")]
-		public string AlsoPlayingTimeInStage;
+		[JsonProperty("alsoPlayingTimeInStage")]
+		public string AlsoPlayingTimeInStage
+		{
+			get { return _alsoPlayingTimeInStage; }
+			set { if (_alsoPlayingTimeInStage != value) { _alsoPlayingTimeInStage = value; NotifyPropertyChanged(nameof(AlsoPlayingTimeInStage)); } }
+		}
 
+		private string _timeInStage;
 		[NumericRange(DisplayName = "Time in Stage", GroupName = "Self", GroupOrder = 4, Description = "Number of rounds since the last time this player lost a hand")]
+		[XmlOrder(180)]
 		[XmlAttribute("timeInStage")]
-		public string TimeInStage;
+		[JsonProperty("timeInStage")]
+		public string TimeInStage
+		{
+			get { return _timeInStage; }
+			set { if (_timeInStage != value) { _timeInStage = value; NotifyPropertyChanged(nameof(TimeInStage)); } }
+		}
 
+		private string _consecutiveLosses;
 		[NumericRange(DisplayName = "Consecutive Losses", GroupName = "Game", GroupOrder = 0, Description = "Number of hands the target player (or this player) has lost in a row")]
+		[XmlOrder(190)]
 		[XmlAttribute("consecutiveLosses")]
-		public string ConsecutiveLosses;
+		[JsonProperty("consecutiveLosses")]
+		public string ConsecutiveLosses
+		{
+			get { return _consecutiveLosses; }
+			set { if (_consecutiveLosses != value) { _consecutiveLosses = value; NotifyPropertyChanged(nameof(ConsecutiveLosses)); } }
+		}
 
+		private string _totalPlaying;
 		[NumericRange(DisplayName = "Total Playing", GroupName = "Table", GroupOrder = 3, Description = "Number of players still in the game", Minimum = 0, Maximum = 5)]
+		[XmlOrder(200)]
 		[XmlAttribute("totalAlive")]
-		public string TotalPlaying;
+		[JsonProperty("totalAlive")]
+		public string TotalPlaying
+		{
+			get { return _totalPlaying; }
+			set { if (_totalPlaying != value) { _totalPlaying = value; NotifyPropertyChanged(nameof(TotalPlaying)); } }
+		}
 
+		private string _totalExposed;
 		[NumericRange(DisplayName = "Total Exposed", GroupName = "Table", GroupOrder = 4, Description = "Number of players who have exposed either their chest or crotch", Minimum = 0, Maximum = 5)]
+		[XmlOrder(210)]
 		[XmlAttribute("totalExposed")]
-		public string TotalExposed;
+		[JsonProperty("totalExposed")]
+		public string TotalExposed
+		{
+			get { return _totalExposed; }
+			set { if (_totalExposed != value) { _totalExposed = value; NotifyPropertyChanged(nameof(TotalExposed)); } }
+		}
 
+		private string _totalNaked;
 		[NumericRange(DisplayName = "Total Naked", GroupName = "Table", GroupOrder = 5, Description = "Number of players who have lost all their clothing, but might still be playing", Minimum = 0, Maximum = 5)]
+		[XmlOrder(220)]
 		[XmlAttribute("totalNaked")]
-		public string TotalNaked;
+		[JsonProperty("totalNaked")]
+		public string TotalNaked
+		{
+			get { return _totalNaked; }
+			set { if (_totalNaked != value) { _totalNaked = value; NotifyPropertyChanged(nameof(TotalNaked)); } }
+		}
 
+		private string _totalMasturbating;
 		[NumericRange(DisplayName = "Total Masturbating", GroupName = "Table", GroupOrder = 6, Description = "Number of players who are currently masturbating", Minimum = 0, Maximum = 5)]
+		[XmlOrder(230)]
 		[XmlAttribute("totalMasturbating")]
-		public string TotalMasturbating;
+		[JsonProperty("totalMasturbating")]
+		public string TotalMasturbating
+		{
+			get { return _totalMasturbating; }
+			set { if (_totalMasturbating != value) { _totalMasturbating = value; NotifyPropertyChanged(nameof(TotalMasturbating)); } }
+		}
 
+		private string _totalFinished;
 		[NumericRange(DisplayName = "Total Finished", GroupName = "Table", GroupOrder = 7, Description = "Number of players who finished masturbating and completely out of the game", Minimum = 0, Maximum = 5)]
+		[XmlOrder(240)]
 		[XmlAttribute("totalFinished")]
-		public string TotalFinished;
+		[JsonProperty("totalFinished")]
+		public string TotalFinished
+		{
+			get { return _totalFinished; }
+			set { if (_totalFinished != value) { _totalFinished = value; NotifyPropertyChanged(nameof(TotalFinished)); } }
+		}
 
+		private string _totalRounds;
 		[NumericRange(DisplayName = "Total Rounds", GroupName = "Game", GroupOrder = 1, Description = "Number of rounds since the game began")]
+		[XmlOrder(250)]
 		[XmlAttribute("totalRounds")]
-		public string TotalRounds;
+		[JsonProperty("totalRounds")]
+		public string TotalRounds
+		{
+			get { return _totalRounds; }
+			set { if (_totalRounds != value) { _totalRounds = value; NotifyPropertyChanged(nameof(TotalRounds)); } }
+		}
 
+		private string _saidMarker;
 		[MarkerCondition(DisplayName = "Said Marker", GroupName = "Self", GroupOrder = 0, Description = "Character has said a marker", ShowPrivate = true)]
+		[XmlOrder(260)]
 		[XmlAttribute("saidMarker")]
-		public string SaidMarker;
+		[JsonProperty("saidMarker")]
+		public string SaidMarker
+		{
+			get { return _saidMarker; }
+			set { if (_saidMarker != value) { _saidMarker = value; NotifyPropertyChanged(nameof(SaidMarker)); } }
+		}
 
+		private string _notSaidMarker;
 		[Marker(DisplayName = "Not Said Marker", GroupName = "Self", GroupOrder = 1, Description = "Character has not said a marker", ShowPrivate = true)]
+		[XmlOrder(270)]
 		[XmlAttribute("notSaidMarker")]
-		public string NotSaidMarker;
+		[JsonProperty("notSaidMarker")]
+		public string NotSaidMarker
+		{
+			get { return _notSaidMarker; }
+			set { if (_notSaidMarker != value) { _notSaidMarker = value; NotifyPropertyChanged(nameof(NotSaidMarker)); } }
+		}
 
+		private string _alsoPlayingSaidMarker;
 		[MarkerCondition(DisplayName = "Also Playing Said Marker", GroupName = "Also Playing", GroupOrder = 2, Description = "Another player has said a marker", ShowPrivate = false, BoundProperties = new string[] { "AlsoPlaying" })]
+		[XmlOrder(280)]
 		[XmlAttribute("alsoPlayingSaidMarker")]
-		public string AlsoPlayingSaidMarker;
+		[JsonProperty("alsoPlayingSaidMarker")]
+		public string AlsoPlayingSaidMarker
+		{
+			get { return _alsoPlayingSaidMarker; }
+			set { if (_alsoPlayingSaidMarker != value) { _alsoPlayingSaidMarker = value; NotifyPropertyChanged(nameof(AlsoPlayingSaidMarker)); } }
+		}
 
+		private string _alsoPlayingNotSaidMarker;
 		[Marker(DisplayName = "Also Playing Not Said Marker", GroupName = "Also Playing", GroupOrder = 3, Description = "Another player has not said a marker", ShowPrivate = false, BoundProperties = new string[] { "AlsoPlaying" })]
+		[XmlOrder(290)]
 		[XmlAttribute("alsoPlayingNotSaidMarker")]
-		public string AlsoPlayingNotSaidMarker;
+		[JsonProperty("alsoPlayingNotSaidMarker")]
+		public string AlsoPlayingNotSaidMarker
+		{
+			get { return _alsoPlayingNotSaidMarker; }
+			set { if (_alsoPlayingNotSaidMarker != value) { _alsoPlayingNotSaidMarker = value; NotifyPropertyChanged(nameof(AlsoPlayingNotSaidMarker)); } }
+		}
 
+		private string _alsoPlayingSayingMarker;
 		[MarkerCondition(DisplayName = "Also Playing Saying Marker", GroupName = "Also Playing", GroupOrder = 4, Description = "Another player is saying a marker at this very moment", ShowPrivate = false, BoundProperties = new string[] { "AlsoPlaying" })]
+		[XmlOrder(300)]
 		[XmlAttribute("alsoPlayingSayingMarker")]
-		public string AlsoPlayingSayingMarker;
+		[JsonProperty("alsoPlayingSayingMarker")]
+		public string AlsoPlayingSayingMarker
+		{
+			get { return _alsoPlayingSayingMarker; }
+			set { if (_alsoPlayingSayingMarker != value) { _alsoPlayingSayingMarker = value; NotifyPropertyChanged(nameof(AlsoPlayingSayingMarker)); } }
+		}
 
+		private string _alsoPlayingSaying;
 		[Text(DisplayName = "Also Playing Saying Text", GroupName = "Also Playing", GroupOrder = 5, Description = "Another player is saying some text at this very moment")]
+		[XmlOrder(310)]
 		[XmlAttribute("alsoPlayingSaying")]
-		public string AlsoPlayingSaying;
+		[JsonProperty("alsoPlayingSaying")]
+		public string AlsoPlayingSaying
+		{
+			get { return _alsoPlayingSaying; }
+			set { if (_alsoPlayingSaying != value) { _alsoPlayingSaying = value; NotifyPropertyChanged(nameof(AlsoPlayingSaying)); } }
+		}
 
+		private string _targetSaidMarker;
 		[MarkerCondition(DisplayName = "Target Said Marker", GroupName = "Target", GroupOrder = 3, Description = "Target has said a marker", ShowPrivate = false, BoundProperties = new string[] { "Target" })]
+		[XmlOrder(320)]
 		[XmlAttribute("targetSaidMarker")]
-		public string TargetSaidMarker;
+		[JsonProperty("targetSaidMarker")]
+		public string TargetSaidMarker
+		{
+			get { return _targetSaidMarker; }
+			set { if (_targetSaidMarker != value) { _targetSaidMarker = value; NotifyPropertyChanged(nameof(TargetSaidMarker)); } }
+		}
 
+		private string _targetNotSaidMarker;
 		[Marker(DisplayName = "Target Not Said Marker", GroupName = "Target", GroupOrder = 4, Description = "Target has not said a marker", ShowPrivate = false, BoundProperties = new string[] { "Target" })]
+		[XmlOrder(330)]
 		[XmlAttribute("targetNotSaidMarker")]
-		public string TargetNotSaidMarker;
+		[JsonProperty("targetNotSaidMarker")]
+		public string TargetNotSaidMarker
+		{
+			get { return _targetNotSaidMarker; }
+			set { if (_targetNotSaidMarker != value) { _targetNotSaidMarker = value; NotifyPropertyChanged(nameof(TargetNotSaidMarker)); } }
+		}
 
+		private string _targetSayingMarker;
 		[MarkerCondition(DisplayName = "Target Saying Marker", GroupName = "Target", GroupOrder = 5, Description = "Target is saying a marker at this very moment", ShowPrivate = false, BoundProperties = new string[] { "Target" })]
+		[XmlOrder(340)]
 		[XmlAttribute("targetSayingMarker")]
-		public string TargetSayingMarker;
+		[JsonProperty("targetSayingMarker")]
+		public string TargetSayingMarker
+		{
+			get { return _targetSayingMarker; }
+			set { if (_targetSayingMarker != value) { _targetSayingMarker = value; NotifyPropertyChanged(nameof(TargetSayingMarker)); } }
+		}
 
+		private string _targetSaying;
 		[Text(DisplayName = "Target Saying Text", GroupName = "Target", GroupOrder = 6, Description = "Target is saying some text at this very moment")]
+		[XmlOrder(350)]
 		[XmlAttribute("targetSaying")]
-		public string TargetSaying;
+		[JsonProperty("targetSaying")]
+		public string TargetSaying
+		{
+			get { return _targetSaying; }
+			set { if (_targetSaying != value) { _targetSaying = value; NotifyPropertyChanged(nameof(TargetSaying)); } }
+		}
 
+		private string _priority;
+		[XmlOrder(360)]
 		[XmlAttribute("priority")]
-		public string CustomPriority;
+		[JsonProperty("priority")]
+		public string CustomPriority
+		{
+			get { return _priority; }
+			set { if (_priority != value) { _priority = value; NotifyPropertyChanged(nameof(CustomPriority)); } }
+		}
 
+		private string _addCharacterTags;
+		[XmlOrder(370)]
 		[XmlAttribute("addCharacterTags")]
-		public string AddCharacterTags;
+		[JsonProperty("addCharacterTags")]
+		public string AddCharacterTags
+		{
+			get { return _addCharacterTags; }
+			set { if (_addCharacterTags != value) { _addCharacterTags = value; NotifyPropertyChanged(nameof(AddCharacterTags)); } }
+		}
 
+		private string _removeCharacterTags;
+		[XmlOrder(380)]
 		[XmlAttribute("removeCharacterTags")]
-		public string RemoveCharacterTags;
+		[JsonProperty("removeCharacterTags")]
+		public string RemoveCharacterTags
+		{
+			get { return _removeCharacterTags; }
+			set { if (_removeCharacterTags != value) { _removeCharacterTags = value; NotifyPropertyChanged(nameof(RemoveCharacterTags)); } }
+		}
 
 		[XmlIgnore]
 		public string TargetStatusType
@@ -221,13 +515,27 @@ namespace SPNATI_Character_Editor
 		}
 
 		[Filter(DisplayName = "Filter (+)", GroupName = "Table", GroupOrder = 0, Description = "Filter based on table conditions. Multiple can be added")]
+		[XmlOrder(390)]
 		[XmlElement("condition")]
-		public List<TargetCondition> Conditions;
+		[JsonProperty("counters")]
+		public ObservableCollection<TargetCondition> Conditions
+		{
+			get { return Get<ObservableCollection<TargetCondition>>(); }
+			set { Set(value); }
+		}
 
 		[Expression(DisplayName = "Variable Test (+)", GroupName = "Game", GroupOrder = 5, Description = "Tests the value of a variable. Multiple can be added", BoundProperties = new string[] { "Target", "AlsoPlaying" })]
+		[XmlOrder(400)]
 		[XmlElement("test")]
-		public List<ExpressionTest> Expressions;
+		[JsonProperty("tests")]
+		public ObservableCollection<ExpressionTest> Expressions
+		{
+			get { return Get<ObservableCollection<ExpressionTest>>(); }
+			set { Set(value); }
+		}
 
+		[JsonProperty("lines")]
+		[XmlOrder(410)]
 		[XmlElement("state")]
 		public List<DialogueLine> Lines;
 
@@ -240,7 +548,11 @@ namespace SPNATI_Character_Editor
 		/// Stages this case appears in
 		/// </summary>
 		[XmlIgnore]
-		public List<int> Stages = new List<int>();
+		public ObservableCollection<int> Stages
+		{
+			get { return Get<ObservableCollection<int>>(); }
+			set { Set(value); }
+		}
 
 		/// <summary>
 		/// Whether this case is considered the "default" for its tag
@@ -252,8 +564,10 @@ namespace SPNATI_Character_Editor
 		{
 			_globalId = s_globalId++;
 			Lines = new List<DialogueLine>();
-			Conditions = new List<TargetCondition>();
-			Expressions = new List<ExpressionTest>();
+			Stages = new ObservableCollection<int>();
+			Conditions = new ObservableCollection<TargetCondition>();
+			Expressions = new ObservableCollection<ExpressionTest>();
+			AlternativeConditions = new ObservableCollection<Case>();
 		}
 
 		public Case(string tag) : this()
@@ -270,138 +584,7 @@ namespace SPNATI_Character_Editor
 			}
 			if (HasConditions)
 			{
-				if (!string.IsNullOrEmpty(Target))
-				{
-					result += string.Format(" (target={0})", Target);
-				}
-				if (!string.IsNullOrEmpty(TargetStage))
-				{
-					result += string.Format(" (target stage={0})", TargetStage);
-				}
-				if (!string.IsNullOrEmpty(TargetStatus))
-				{
-					result += string.Format(" (target {0})", TargetStatus.Replace("_", " "));
-				}
-				if (!string.IsNullOrEmpty(TargetHand))
-				{
-					result += string.Format(" (target hand={0})", TargetHand);
-				}
-				if (!string.IsNullOrEmpty(TargetTimeInStage))
-				{
-					result += string.Format(" (after {0} rounds in stage)", GUIHelper.RangeToString(TargetTimeInStage));
-				}
-				if (!string.IsNullOrEmpty(TargetLayers))
-				{
-					result += $" (layers remaining={TargetLayers})";
-				}
-				if (!string.IsNullOrEmpty(Filter))
-				{
-					result += string.Format(" (filter={0})", Filter);
-				}
-				if (!string.IsNullOrEmpty(AlsoPlaying))
-				{
-					result += string.Format(" (playing w/{0})", AlsoPlaying);
-				}
-				if (!string.IsNullOrEmpty(AlsoPlayingStage))
-				{
-					result += string.Format(" (playing w/stage={0})", AlsoPlayingStage);
-				}
-				if (!string.IsNullOrEmpty(AlsoPlayingTimeInStage))
-				{
-					result += string.Format(" (after {0} rounds in stage)", GUIHelper.RangeToString(AlsoPlayingTimeInStage));
-				}
-				if (!string.IsNullOrEmpty(HasHand))
-				{
-					result += string.Format(" (hand={0})", HasHand);
-				}
-				if (Conditions.Count > 0)
-				{
-					result += string.Format(" ({0})", string.Join(",", Conditions));
-				}
-				if (Expressions.Count > 0)
-				{
-					result += $" ({string.Join(",", Expressions)})";
-				}
-				if (!string.IsNullOrEmpty(TotalFemales))
-				{
-					result += string.Format(" ({0} females)", GUIHelper.RangeToString(TotalFemales));
-				}
-				if (!string.IsNullOrEmpty(TotalMales))
-				{
-					result += string.Format(" ({0} males)", GUIHelper.RangeToString(TotalMales));
-				}
-				if (!string.IsNullOrEmpty(TotalRounds))
-				{
-					result += string.Format(" ({0} overall rounds)", GUIHelper.RangeToString(TotalRounds));
-				}
-				if (!string.IsNullOrEmpty(TimeInStage))
-				{
-					result += string.Format(" (after {0} rounds in own stage)", GUIHelper.RangeToString(TimeInStage));
-				}
-				if (!string.IsNullOrEmpty(ConsecutiveLosses))
-				{
-					result += string.Format(" ({0} losses in a row)", GUIHelper.RangeToString(ConsecutiveLosses));
-				}
-				if (!string.IsNullOrEmpty(TotalPlaying))
-				{
-					result += string.Format(" ({0} playing)", GUIHelper.RangeToString(TotalPlaying));
-				}
-				if (!string.IsNullOrEmpty(TotalExposed))
-				{
-					result += string.Format(" ({0} exposed)", GUIHelper.RangeToString(TotalExposed));
-				}
-				if (!string.IsNullOrEmpty(TotalNaked))
-				{
-					result += string.Format(" ({0} naked)", GUIHelper.RangeToString(TotalNaked));
-				}
-				if (!string.IsNullOrEmpty(TotalMasturbating))
-				{
-					result += string.Format(" ({0} finishing)", GUIHelper.RangeToString(TotalMasturbating));
-				}
-				if (!string.IsNullOrEmpty(TotalFinished))
-				{
-					result += string.Format(" ({0} finished)", GUIHelper.RangeToString(TotalFinished));
-				}
-				if (!string.IsNullOrEmpty(SaidMarker))
-				{
-					result += string.Format(" (said {0})", SaidMarker);
-				}
-				if (!string.IsNullOrEmpty(NotSaidMarker))
-				{
-					result += string.Format(" (not said {0})", NotSaidMarker);
-				}
-				if (!string.IsNullOrEmpty(TargetSaidMarker))
-				{
-					result += string.Format(" (target said {0})", TargetSaidMarker);
-				}
-				if (!string.IsNullOrEmpty(TargetNotSaidMarker))
-				{
-					result += string.Format(" (target not said {0})", TargetNotSaidMarker);
-				}
-				if (!string.IsNullOrEmpty(TargetSayingMarker))
-				{
-					result += string.Format(" (target saying {0})", TargetSayingMarker);
-				}
-				if (!string.IsNullOrEmpty(TargetSaying))
-				{
-					result += string.Format(" (target saying \"{0}\")", TargetSaying);
-				}
-				if (!string.IsNullOrEmpty(AlsoPlayingSaidMarker))
-				{
-					result += string.Format(" (other said {0})", AlsoPlayingSaidMarker);
-				}
-				if (!string.IsNullOrEmpty(AlsoPlayingNotSaidMarker))
-				{
-					result += string.Format(" (other not said {0})", AlsoPlayingNotSaidMarker);
-				}
-				if (!string.IsNullOrEmpty(AlsoPlayingSayingMarker))
-				{
-					result += string.Format(" (other saying {0})", AlsoPlayingSayingMarker);
-				}
-				if (!string.IsNullOrEmpty(AlsoPlayingSaying))
-				{
-					result += string.Format(" (other saying \"{0}\")", AlsoPlayingSaying);
-				}
+				result += " " + ToConditionsString(false);
 			}
 			if (string.IsNullOrEmpty(Hidden))
 			{
@@ -414,6 +597,163 @@ namespace SPNATI_Character_Editor
 			return result;
 		}
 
+		public string ToConditionsString(bool excludeTarget)
+		{
+			List<string> results = new List<string>();
+			foreach (Case alternate in AlternativeConditions)
+			{
+				results.Add(alternate.ToConditionsString(excludeTarget));
+			}
+			List<string> result = new List<string>();
+			if (!string.IsNullOrEmpty(Target) && !excludeTarget)
+			{
+				result.Add(string.Format("(target={0})", Target));
+			}
+			if (!string.IsNullOrEmpty(TargetStage))
+			{
+				result.Add(string.Format("(target stage={0})", TargetStage));
+			}
+			if (!string.IsNullOrEmpty(TargetStatus))
+			{
+				result.Add(string.Format("(target {0})", TargetStatus.Replace("_", " ")));
+			}
+			if (!string.IsNullOrEmpty(TargetHand))
+			{
+				result.Add(string.Format("(target hand={0})", TargetHand));
+			}
+			if (!string.IsNullOrEmpty(TargetTimeInStage))
+			{
+				result.Add(string.Format("(after {0} rounds in stage)", GUIHelper.RangeToString(TargetTimeInStage)));
+			}
+			if (!string.IsNullOrEmpty(TargetLayers))
+			{
+				result.Add($"(layers remaining={TargetLayers})");
+			}
+			if (!string.IsNullOrEmpty(Filter))
+			{
+				result.Add(string.Format("(filter={0})", Filter));
+			}
+			if (!string.IsNullOrEmpty(AlsoPlaying) && (!excludeTarget || !string.IsNullOrEmpty(Target)))
+			{
+				result.Add(string.Format("(playing w/{0})", AlsoPlaying));
+			}
+			if (!string.IsNullOrEmpty(AlsoPlayingStage))
+			{
+				result.Add(string.Format("(playing w/stage={0})", AlsoPlayingStage));
+			}
+			if (!string.IsNullOrEmpty(AlsoPlayingTimeInStage))
+			{
+				result.Add(string.Format("(after {0} rounds in stage)", GUIHelper.RangeToString(AlsoPlayingTimeInStage)));
+			}
+			if (!string.IsNullOrEmpty(AlsoPlayingHand))
+			{
+				result.Add(string.Format("(playing w/hand={0})", AlsoPlayingHand));
+			}
+			if (!string.IsNullOrEmpty(HasHand))
+			{
+				result.Add(string.Format("(hand={0})", HasHand));
+			}
+			if (Conditions.Count > 0)
+			{
+				result.Add(string.Format("({0})", string.Join(",", Conditions)));
+			}
+			if (!string.IsNullOrEmpty(TotalFemales))
+			{
+				result.Add(string.Format("({0} females)", GUIHelper.RangeToString(TotalFemales)));
+			}
+			if (!string.IsNullOrEmpty(TotalMales))
+			{
+				result.Add(string.Format("({0} males)", GUIHelper.RangeToString(TotalMales)));
+			}
+			if (!string.IsNullOrEmpty(TotalRounds))
+			{
+				result.Add(string.Format("({0} overall rounds)", GUIHelper.RangeToString(TotalRounds)));
+			}
+			if (!string.IsNullOrEmpty(TimeInStage))
+			{
+				result.Add(string.Format("(after {0} rounds in own stage)", GUIHelper.RangeToString(TimeInStage)));
+			}
+			if (!string.IsNullOrEmpty(ConsecutiveLosses))
+			{
+				result.Add(string.Format("({0} losses in a row)", GUIHelper.RangeToString(ConsecutiveLosses)));
+			}
+			if (!string.IsNullOrEmpty(TotalPlaying))
+			{
+				result.Add(string.Format("({0} playing)", GUIHelper.RangeToString(TotalPlaying)));
+			}
+			if (!string.IsNullOrEmpty(TotalExposed))
+			{
+				result.Add(string.Format("({0} exposed)", GUIHelper.RangeToString(TotalExposed)));
+			}
+			if (!string.IsNullOrEmpty(TotalNaked))
+			{
+				result.Add(string.Format("({0} naked)", GUIHelper.RangeToString(TotalNaked)));
+			}
+			if (!string.IsNullOrEmpty(TotalMasturbating))
+			{
+				result.Add(string.Format("({0} finishing)", GUIHelper.RangeToString(TotalMasturbating)));
+			}
+			if (!string.IsNullOrEmpty(TotalFinished))
+			{
+				result.Add(string.Format("({0} finished)", GUIHelper.RangeToString(TotalFinished)));
+			}
+			if (!string.IsNullOrEmpty(SaidMarker))
+			{
+				result.Add(string.Format("(said {0})", SaidMarker));
+			}
+			if (!string.IsNullOrEmpty(NotSaidMarker))
+			{
+				result.Add(string.Format("(not said {0})", NotSaidMarker));
+			}
+			if (!string.IsNullOrEmpty(TargetSaidMarker))
+			{
+				result.Add(string.Format("(target said {0})", TargetSaidMarker));
+			}
+			if (!string.IsNullOrEmpty(TargetNotSaidMarker))
+			{
+				result.Add(string.Format("(target not said {0})", TargetNotSaidMarker));
+			}
+			if (!string.IsNullOrEmpty(TargetSayingMarker))
+			{
+				result.Add(string.Format("(target saying {0})", TargetSayingMarker));
+			}
+			if (!string.IsNullOrEmpty(TargetSaying))
+			{
+				result.Add(string.Format("(target saying \"{0}\")", TargetSaying));
+			}
+			if (!string.IsNullOrEmpty(AlsoPlayingSaidMarker))
+			{
+				result.Add(string.Format("(other said {0})", AlsoPlayingSaidMarker));
+			}
+			if (!string.IsNullOrEmpty(AlsoPlayingNotSaidMarker))
+			{
+				result.Add(string.Format("(other not said {0})", AlsoPlayingNotSaidMarker));
+			}
+			if (!string.IsNullOrEmpty(AlsoPlayingSayingMarker))
+			{
+				result.Add(string.Format("(other saying {0})", AlsoPlayingSayingMarker));
+			}
+			if (!string.IsNullOrEmpty(AlsoPlayingSaying))
+			{
+				result.Add(string.Format("(other saying \"{0}\")", AlsoPlayingSaying));
+			}
+			if (Expressions.Count > 0)
+			{
+				result.Add($"({string.Join(",", Expressions)})");
+			}
+			if (OneShotId > 0)
+			{
+				result.Add("(play once)");
+			}
+			results.Add(string.Join(" ", result));
+			string conditions = string.Join(" OR ", results);
+			if (AlternativeConditions.Count > 0)
+			{
+				conditions = "*" + conditions;
+			}
+			return conditions;
+		}
+
 		/// <summary>
 		/// Copies the case except stages and lines
 		/// </summary>
@@ -421,22 +761,25 @@ namespace SPNATI_Character_Editor
 		public Case CopyConditions()
 		{
 			Case copy = new Case();
-			foreach (FieldInfo field in this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance))
+			foreach (MemberInfo field in this.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance))
 			{
-				if (field.FieldType == typeof(string))
+				if (field.MemberType == MemberTypes.Field || field.MemberType == MemberTypes.Property)
 				{
-					field.SetValue(copy, field.GetValue(this));
+					if (field.GetDataType() == typeof(string) || field.GetDataType() == typeof(int))
+					{
+						field.SetValue(copy, field.GetValue(this));
+					}
 				}
 			}
 
 			//Since it's just a shallow collection, need to break references to objects
-			copy.Conditions = new List<TargetCondition>();
+			copy.Conditions = new ObservableCollection<TargetCondition>();
 			foreach (TargetCondition condition in Conditions)
 			{
 				copy.Conditions.Add(condition.Copy());
 			}
 
-			copy.Expressions = new List<ExpressionTest>();
+			copy.Expressions = new ObservableCollection<ExpressionTest>();
 			foreach (ExpressionTest test in Expressions)
 			{
 				copy.Expressions.Add(test.Copy());
@@ -455,16 +798,24 @@ namespace SPNATI_Character_Editor
 			{
 				copy.Lines.Add(Lines[i].Copy());
 			}
+			copy.AlternativeConditions = new ObservableCollection<Case>();
+			foreach (Case alternate in AlternativeConditions)
+			{
+				copy.AlternativeConditions.Add(alternate.Copy());
+			}
 			return copy;
 		}
 
 		public void ClearEmptyValues()
 		{
-			foreach (FieldInfo field in this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance))
+			foreach (MemberInfo field in this.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance))
 			{
-				if (field.FieldType == typeof(string) && (string)field.GetValue(this) == "")
+				if (field.MemberType == MemberTypes.Field || field.MemberType == MemberTypes.Property)
 				{
-					field.SetValue(this, null);
+					if (field.GetDataType() == typeof(string) && (string)field.GetValue(this) == "")
+					{
+						field.SetValue(this, null);
+					}
 				}
 			}
 
@@ -483,7 +834,7 @@ namespace SPNATI_Character_Editor
 			int totalPriority = 0;
 			if (!string.IsNullOrEmpty(Hidden))
 			{
-				return int.MaxValue;
+				return int.MinValue;
 			}
 			if (!string.IsNullOrEmpty(CustomPriority))
 			{
@@ -594,28 +945,133 @@ namespace SPNATI_Character_Editor
 		{
 			if (!matchAll)
 			{
-				for (int i = 0; i < Stages.Count; i++)
-				{
-					if (other.Stages.Contains(Stages[i]))
-					{
-						return true;
-					}
-				}
+				for (int i = 0; i < Stages.Count; i++)
+				{
+					if (other.Stages.Contains(Stages[i]))
+					{
+						return true;
+					}
+				}
+				return false;
+			}
+
+			if (other.Stages.Count != Stages.Count)
+			{
+				return false;
+			}
+
+			for (int i = 0; i < other.Stages.Count; i++)
+			{
+				if (other.Stages[i] != Stages[i])
+				{
+					return false;
+				}
+			}
+			return true;
+		}
+
+		/// <summary>
+		/// Gets whether the lines in this case are identical to another
+		/// </summary>
+		/// <param name="other"></param>
+		/// <returns></returns>
+		public bool MatchesLines(Case other)
+		{
+			if (Lines.Count != other.Lines.Count)
+			{
+				return false;
+			}
+			for (int i = 0; i < Lines.Count; i++)
+			{
+				if (Lines[i].GetHashCode() != other.Lines[i].GetHashCode())
+				{
+					return false;
+				}
+			}
+			return true;
+		}
+
+		private int _conditionHash;
+		protected override void OnPropertyChanged(string propName)
+		{
+			_conditionHash = 0;
+		}
+
+		private int GetConditionHash()
+		{
+			if (_conditionHash > 0)
+			{
+				return _conditionHash;
+			}
+			int hash = (Target ?? "").GetHashCode();
+			hash = (hash * 397) ^ (TargetHand ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TargetStage ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TargetTimeInStage ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TargetStatus ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TargetLayers ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TargetStartingLayers ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (AlsoPlaying ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (AlsoPlayingHand ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (AlsoPlayingStage ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (AlsoPlayingTimeInStage ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (HasHand ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (Filter ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TimeInStage ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TotalFemales ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TotalMales ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TotalPlaying ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TotalExposed ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TotalMasturbating ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TotalNaked ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TotalFinished ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TotalRounds ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (SaidMarker ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (NotSaidMarker ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TargetSaidMarker ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TargetNotSaidMarker ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TargetSayingMarker ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (AlsoPlayingSaidMarker ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (AlsoPlayingNotSaidMarker ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (AlsoPlayingSayingMarker ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (ConsecutiveLosses ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TargetSaying ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (AlsoPlayingSaying ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (AddCharacterTags ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (RemoveCharacterTags ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (CustomPriority ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (Hidden ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (OneShotId > 0 ? OneShotId : -1);
+			_conditionHash = hash;
+			return hash;
+		}
+
+		/// <summary>
+		/// Gets whether this case matches another in everything but conditions
+		/// </summary>
+		/// <param name="other"></param>
+		/// <returns></returns>
+		public bool MatchesNonConditions(Case other)
+		{
+			if (other == this)
+				return true;
+			if (Tag != other.Tag)
+				return false;
+
+			if (other.Lines.Count != Lines.Count)
+			{
 				return false;
 			}
-
-			if (other.Stages.Count != Stages.Count)
+			if (other.GetLineCode() != GetLineCode())
 			{
 				return false;
 			}
-
-			for (int i = 0; i < other.Stages.Count; i++)
+			if (other.RemoveCharacterTags != RemoveCharacterTags ||
+				other.AddCharacterTags != AddCharacterTags ||
+				other.CustomPriority != CustomPriority)
 			{
-				if (other.Stages[i] != Stages[i])
-				{
-					return false;
-				}
+				return false;
 			}
+
 			return true;
 		}
 
@@ -630,42 +1086,8 @@ namespace SPNATI_Character_Editor
 				return true;
 			if (Tag != other.Tag)
 				return false;
-			bool sameFilters = Target == other.Target &&
-				TargetHand == other.TargetHand &&
-				TargetStage == other.TargetStage &&
-				TargetTimeInStage == other.TargetTimeInStage &&
-				TargetLayers == other.TargetLayers &&
-				TargetStatus == other.TargetStatus &&
-				TargetStartingLayers == other.TargetStartingLayers &&
-				AlsoPlaying == other.AlsoPlaying &&
-				AlsoPlayingHand == other.AlsoPlayingHand &&
-				AlsoPlayingStage == other.AlsoPlayingStage &&
-				AlsoPlayingTimeInStage == other.AlsoPlayingTimeInStage &&
-				HasHand == other.HasHand &&
-				Filter == other.Filter &&
-				TimeInStage == other.TimeInStage &&
-				TotalFemales == other.TotalFemales &&
-				TotalMales == other.TotalMales &&
-				TotalPlaying == other.TotalPlaying &&
-				TotalExposed == other.TotalExposed &&
-				TotalMasturbating == other.TotalMasturbating &&
-				TotalNaked == other.TotalNaked &&
-				TotalFinished == other.TotalFinished &&
-				TotalRounds == other.TotalRounds &&
-				SaidMarker == other.SaidMarker &&
-				NotSaidMarker == other.NotSaidMarker &&
-				TargetSaidMarker == other.TargetSaidMarker &&
-				TargetNotSaidMarker == other.TargetNotSaidMarker &&
-				TargetSayingMarker == other.TargetSayingMarker &&
-				AlsoPlayingSaidMarker == other.AlsoPlayingSaidMarker &&
-				AlsoPlayingNotSaidMarker == other.AlsoPlayingNotSaidMarker &&
-				AlsoPlayingSayingMarker == other.AlsoPlayingSayingMarker &&
-				ConsecutiveLosses == other.ConsecutiveLosses &&
-				TargetSaying == other.TargetSaying &&
-				AlsoPlayingSaying == other.AlsoPlayingSaying &&
-				AddCharacterTags == other.AddCharacterTags &&
-				RemoveCharacterTags == other.RemoveCharacterTags &&
-				Hidden == other.Hidden;
+
+			bool sameFilters = (GetConditionHash() == other.GetConditionHash());
 			if (!sameFilters)
 				return false;
 
@@ -675,7 +1097,7 @@ namespace SPNATI_Character_Editor
 			{
 				TargetCondition c1 = Conditions[i];
 				TargetCondition c2 = other.Conditions[i];
-				if (c1.FilterTag != c2.FilterTag || c1.Count != c2.Count || c1.Status != c2.Status || c1.Gender != c2.Gender)
+				if (!c1.Equals(c2))
 					return false;
 			}
 			if (other.Expressions.Count != Expressions.Count)
@@ -758,6 +1180,23 @@ namespace SPNATI_Character_Editor
 			}
 		}
 
+		[XmlIgnore]
+		public ObservableCollection<Case> AlternativeConditions
+		{
+			get { return Get<ObservableCollection<Case>>(); }
+			set { Set(value); }
+		}
+
+		public IEnumerable<Case> GetConditionSets()
+		{
+			yield return this;
+			foreach (Case alternate in AlternativeConditions)
+			{
+				yield return alternate;
+			}
+		}
+
+
 		/// <summary>
 		/// Gets whether this case has any targeted dialogue that is based on game state
 		/// </summary>
@@ -791,49 +1230,10 @@ namespace SPNATI_Character_Editor
 		public int GetCode()
 		{
 			int hash = Tag.GetHashCode();
-			hash = (hash * 397) ^ (Target ?? "").GetHashCode();
-			hash = (hash * 397) ^ (TargetHand ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TargetStage ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TargetStatus ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TargetLayers ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TargetStartingLayers ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (AlsoPlaying ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (AlsoPlayingHand ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (AlsoPlayingStage ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TotalFemales ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TotalMales ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (HasHand ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (Filter ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TargetTimeInStage ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (AlsoPlayingTimeInStage ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TimeInStage ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (ConsecutiveLosses ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TotalExposed ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TotalPlaying ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TotalNaked ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TotalMasturbating ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TotalFinished ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TotalRounds ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (SaidMarker ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (NotSaidMarker ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TargetSaidMarker ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TargetNotSaidMarker ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TargetSayingMarker ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (AlsoPlayingSaidMarker ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (AlsoPlayingNotSaidMarker ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (AlsoPlayingSayingMarker ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (TargetSaying ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (AlsoPlayingSaying ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (CustomPriority ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (Hidden ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (AddCharacterTags ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (RemoveCharacterTags ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ GetConditionHash();
 			foreach (var condition in Conditions)
 			{
-				hash = (hash * 397) ^ (condition.FilterTag ?? string.Empty).GetHashCode();
-				hash = (hash * 397) ^ (condition.Gender ?? string.Empty).GetHashCode();
-				hash = (hash * 397) ^ (condition.Status ?? string.Empty).GetHashCode();
-				hash = (hash * 397) ^ condition.Count.GetHashCode();
+				hash = (hash * 397) ^ condition.GetHashCode();
 			}
 			foreach (ExpressionTest expr in Expressions)
 			{
@@ -842,6 +1242,20 @@ namespace SPNATI_Character_Editor
 			return hash;
 		}
 
+		/// <summary>
+		/// Returns a unique hash for this combination of lines
+		/// </summary>
+		/// <returns></returns>
+		public int GetLineCode()
+		{
+			int hash = 0;
+			foreach (DialogueLine line in Lines)
+			{
+				hash = (hash * 397) ^ line.GetHashCode();
+			}
+			return hash;
+		}
+
 		public int CompareTo(Case other)
 		{
 			int comparison = Tag.CompareTo(other.Tag);
@@ -1062,6 +1476,19 @@ namespace SPNATI_Character_Editor
 			response.TotalFinished = TotalFinished;
 			response.TotalRounds = TotalRounds;
 
+			//special cases
+			if (Tag == "must_masturbate_first")
+			{
+				response.TotalMasturbating = "0";
+				response.TotalFinished = "0";
+			}
+
+			foreach (Case alternate in AlternativeConditions)
+			{
+				Case alternateResponse = alternate.CreateResponse(speaker, responder);
+				response.AlternativeConditions.Add(alternateResponse);
+			}
+
 			return response;
 		}
 
@@ -1224,35 +1651,32 @@ namespace SPNATI_Character_Editor
 			other.AlsoPlayingNotSaidMarker = NotSaidMarker;
 			other.AlsoPlayingSaidMarker = SaidMarker;
 
-			//If all lines set the same marker, use that marker in targetSayingMarker
-			string marker = null;
-			foreach (DialogueLine line in Lines)
+			//If all lines set the same marker, use that marker in alsoPlayingSayingMarker
+			if (Lines.Count > 0)
 			{
-				if (line.Marker != null)
+				string marker = Lines[0].Marker;
+				for (int l = 1; l < Lines.Count; l++)
 				{
-					if (marker == null)
-					{
-						marker = line.Marker;
-					}
-					else if (marker != line.Marker)
+					DialogueLine line = Lines[l];
+					if (line.Marker != marker)
 					{
 						marker = null;
 						break;
 					}
 				}
-				else if (marker != null)
-				{
-					marker = null;
-					break;
-				}
-			}
-			if (!string.IsNullOrEmpty(marker))
-			{
-				if (marker.StartsWith("+") || marker.StartsWith("-"))
+				if (!string.IsNullOrEmpty(marker))
 				{
-					marker = marker.Substring(1);
+					if (marker.StartsWith("+") || marker.StartsWith("-"))
+					{
+						marker = marker.Substring(1);
+					}
+					other.AlsoPlayingSayingMarker = marker;
+					if (other.AlsoPlayingNotSaidMarker == marker)
+					{
+						//if they had a not said marker for the same thing, clear that
+						other.AlsoPlayingNotSaidMarker = null;
+					}
 				}
-				other.AlsoPlayingSayingMarker = marker;
 			}
 		}
 
@@ -1336,32 +1760,33 @@ namespace SPNATI_Character_Editor
 			other.TargetNotSaidMarker = NotSaidMarker;
 			other.TargetSaidMarker = SaidMarker;
 
-			//If all lines set the same marker, use that marker in targetSayingMarker
-			string marker = null;
-			foreach (DialogueLine line in Lines)
+			//If all lines set the same marker, use that marker in alsoPlayingSayingMarker
+			if (Lines.Count > 0)
 			{
-				if (line.Marker != null)
+				string marker = Lines[0].Marker;
+				for (int l = 1; l < Lines.Count; l++)
 				{
-					if (marker == null)
-					{
-						marker = line.Marker;
-					}
-					else if (marker != line.Marker)
+					DialogueLine line = Lines[l];
+					if (line.Marker != marker)
 					{
 						marker = null;
 						break;
 					}
 				}
-				else if (marker != null)
+				if (!string.IsNullOrEmpty(marker))
 				{
-					marker = null;
-					break;
+					if (marker.StartsWith("+") || marker.StartsWith("-"))
+					{
+						marker = marker.Substring(1);
+					}
+					other.TargetSayingMarker = marker;
+					if (other.TargetNotSaidMarker == marker)
+					{
+						//if they had a not said marker for the same thing, clear that
+						other.TargetNotSaidMarker = null;
+					}
 				}
 			}
-			if (!string.IsNullOrEmpty(marker))
-			{
-				other.TargetSayingMarker = marker;
-			}
 		}
 
 		/// <summary>
@@ -1374,11 +1799,30 @@ namespace SPNATI_Character_Editor
 		private string GetLayerType(Character speaker, int stage)
 		{
 			Clothing layer = speaker.Wardrobe[speaker.Layers - stage - 1];
-			if (layer.Type == "extra")
+			string layerType = layer.Type;
+			if (layer.Type == "major" && layer.Position != "both")
+			{
+				//if this is the last major and there are no importants, treat as important
+				bool foundImportant = false;
+				for (int l = speaker.Layers - stage - 2; l >= 0; l--)
+				{
+					Clothing other = speaker.Wardrobe[l];
+					if ((other.Type == "important" || other.Type == "major") && (other.Position == "both" || other.Position == layer.Position))
+					{
+						foundImportant = true;
+						break;
+					}
+				}
+				if (!foundImportant)
+				{
+					layerType = "important";
+				}
+			}
+			if (layerType == "extra")
 			{
 				return "accessory";
 			}
-			else if (layer.Type == "important")
+			else if (layerType == "important")
 			{
 				if (layer.Position == "lower")
 				{
@@ -1389,7 +1833,7 @@ namespace SPNATI_Character_Editor
 					return "chest";
 				}
 			}
-			return layer.Type;
+			return layerType;
 		}
 
 		/// <summary>
@@ -1538,6 +1982,10 @@ namespace SPNATI_Character_Editor
 				{
 					return "stripped";
 				}
+				else if (tag == "game_over_defeat")
+				{
+					return "game_over_victory";
+				}
 			}
 
 			if (tag == "good_hand" || tag == "okay_hand" || tag == "bad_hand")
@@ -1786,355 +2234,60 @@ namespace SPNATI_Character_Editor
 				return false;
 			}
 		}
-	}
-
-	public class TargetCondition
-	{
-		[XmlAttribute("count")]
-		/// <summary>
-		/// Number of characters needing the filter tag
-		/// </summary>
-		public string Count;
-
-		[XmlAttribute("filter")]
-		/// <summary>
-		/// Tag to condition on
-		/// </summary>
-		public string FilterTag;
-
-		[XmlAttribute("gender")]
-		public string Gender;
-
-		[Status(DisplayName = "Status", Description = "Status of the characters to match", GroupOrder = 10)]
-		[XmlAttribute("status")]
-		public string Status;
-
-		[DefaultValue("")]
-		[RecordSelect(DisplayName = "Role", GroupOrder = 20, Description = "What type of characters to target", RecordType = typeof(FilterRole))]
-		[XmlAttribute("role")]
-		public string Role;
-
-		[DefaultValue("")]
-		[RecordSelect(DisplayName = "Character", GroupOrder = 30, Description = "Character to target", RecordType = typeof(Character))]
-		[XmlAttribute("character")]
-		public string FilterId;
-
-		[DefaultValue("")]
-		[StageSelect(DisplayName = "Stage", GroupOrder = 40, Description = "Stage to target", BoundProperties = new string[] { "FilterId" })]
-		[XmlAttribute("stage")]
-		public string FilterStage;
-
-		[DefaultValue("")]
-		[Text(DisplayName = "Variable", GroupOrder = 0, Description = "Name of variable to store a target that this condition matches")]
-		[XmlAttribute("var")]
-		public string Variable;
-
-		public static readonly KeyValuePair<string, string>[] StatusTypes = {
-			new KeyValuePair<string, string>(null, ""),
-			new KeyValuePair<string, string>("lost_some", "Lost something"),
-			new KeyValuePair<string, string>("mostly_clothed", "Lost only accessories"),
-			new KeyValuePair<string, string>("decent", "Still covered by major articles"),
-			new KeyValuePair<string, string>("exposed", "Chest and/or crotch visible"),
-			new KeyValuePair<string, string>("chest_visible", "Chest visible"),
-			new KeyValuePair<string, string>("crotch_visible", "Crotch visible"),
-			new KeyValuePair<string, string>("topless", "Topless (not naked)"),
-			new KeyValuePair<string, string>("bottomless", "Bottomless (not naked)"),
-			new KeyValuePair<string, string>("naked", "Naked (fully exposed)"),
-			new KeyValuePair<string, string>("lost_all", "Lost all layers"),
-			new KeyValuePair<string, string>("alive", "Still in the game"),
-			new KeyValuePair<string, string>("masturbating", "Masturbating"),
-			new KeyValuePair<string, string>("finished", "Finished masturbating")
-		};
-
-		public TargetCondition()
-		{
-		}
 
-		public TargetCondition(string tag, string gender, string status, string count)
+		[OnDeserialized]
+		public void OnDeserialized(StreamingContext context)
 		{
-			FilterTag = tag;
-			Gender = gender;
-			Status = status;
-			Count = count;
+			if (!string.IsNullOrEmpty(Stage))
+			{
+				Tuple<int, int> interval = GUIHelper.ToInterval(Stage);
+				for (int i = interval.Item1; i <= interval.Item2; i++)
+				{
+					Stages.Add(i);
+				}
+			}
 		}
 
-		public TargetCondition(string serializedData, string count)
+		/// <summary>
+		/// Gets a list of markers that this case assumes are being said
+		/// </summary>
+		/// <returns></returns>
+		public List<string> GetMarkers()
 		{
-			Count = count;
-
-			string[] parts = serializedData.Split('&');
-			foreach (string part in parts)
+			List<string> list = new List<string>();
+			if (!string.IsNullOrEmpty(SaidMarker) && (!SaidMarker.Contains("!=") || SaidMarker.EndsWith("!=0")) && !SaidMarker.EndsWith("==0"))
 			{
-				if (part.Contains(";"))
+				int splitIndex = SaidMarker.IndexOfAny(new char[] { '=', '>', '<', '!' });
+				if (splitIndex > 0)
 				{
-					string[] pieces = part.Split(new char[] { ';' }, 2);
-					if (pieces.Length == 2)
-					{
-						string key = pieces[0];
-						string value = pieces[1];
-						switch (key)
-						{
-							case "var":
-								Variable = value;
-								break;
-							case "stage":
-								FilterStage = value;
-								break;
-							case "character":
-								FilterId = value;
-								break;
-							case "role":
-								Role = value;
-								break;
-						}
-					}
+					list.Add(SaidMarker.Substring(0, splitIndex));
 				}
 				else
 				{
-					if (part == "male" || part == "female")
-					{
-						Gender = part;
-					}
-					else if (part != "" && Array.Exists(StatusTypes, t => t.Key == part || "not_" + t.Key == part))
-					{
-						Status = part;
-					}
-					else
-					{
-						FilterTag = part;
-					}
+					list.Add(SaidMarker);
 				}
 			}
-		}
-
-		public void ClearEmptyValues()
-		{
-			if (FilterTag == "")
-				FilterTag = null;
-			if (Gender == "")
-				Gender = null;
-			if (Status == "")
-				Status = null;
-		}
-
-		public TargetCondition Copy()
-		{
-			TargetCondition copy = MemberwiseClone() as TargetCondition;
-			return copy;
-		}
-
-		public string Serialize()
-		{
-			List<string> parts = new List<string>();
-			if (!string.IsNullOrEmpty(Status))
-			{
-				parts.Add(Status);
-			}
-			if (!string.IsNullOrEmpty(Gender))
-			{
-				parts.Add(Gender);
-			}
-			if (!string.IsNullOrEmpty(FilterTag))
-			{
-				parts.Add(FilterTag);
-			}
-			if (!string.IsNullOrEmpty(Role))
-			{
-				parts.Add("role:" + Role);
-			}
-			if (!string.IsNullOrEmpty(FilterId))
-			{
-				parts.Add("character:" + FilterId);
-			}
-			if (!string.IsNullOrEmpty(FilterStage))
-			{
-				parts.Add("stage:" + FilterStage);
-			}
-			if (!string.IsNullOrEmpty(Variable))
-			{
-				parts.Add("var:" + FilterId);
-			}
-			string data = string.Format("count-{1}:{0}", Count, string.Join("&", parts));
-			return data;
-		}
-
-		public override string ToString()
-		{
-			string str = GUIHelper.RangeToString(Count);
-			if (FilterTag == null && Status == null && Gender == null && !HasAdvancedConditions)
-			{
-				str += " players";
-			}
-			else
+			foreach (ExpressionTest test in Expressions)
 			{
-				if (!string.IsNullOrEmpty(Role))
+				if (test.Expression.StartsWith("~marker.") || test.Expression.StartsWith("~self.marker."))
 				{
-					switch (Role)
+					if (test.Operator != "!=" || (test.Operator == "!=" && test.Value == "0"))
 					{
-						case "target":
-							str += " target";
-							break;
-						case "opp":
-							str += " opposing";
-							break;
-						case "other":
-							str += " other";
-							break;
+						int dot = test.Expression.LastIndexOf('.');
+						if (dot >= 0)
+						{
+							string marker = test.Expression.Substring(dot + 1, test.Expression.Length - (dot + 2));
+							list.Add(marker);
+						}
 					}
 				}
-				if (Status != null)
-				{
-					str += " " + Status.Replace("_", " ");
-				}
-				if (!string.IsNullOrEmpty(Gender))
-				{
-					str += " " + Gender + (FilterTag != null ? "" : "s");
-				}
-				if (FilterTag != null)
-				{
-					str += " " + FilterTag;
-				}
-				if (!string.IsNullOrEmpty(FilterId))
-				{
-					str += $" id: {FilterId}";
-				}
-				if (!string.IsNullOrEmpty(FilterStage))
-				{
-					str += $" stage: {FilterStage}";
-				}
-				if (!string.IsNullOrEmpty(Variable))
-				{
-					str += $" => {Variable}";
-				}
-			}
-			return str;
-		}
-
-		public int GetPriority()
-		{
-			int priority = 0;
-			if (!string.IsNullOrEmpty(FilterId))
-			{
-				priority += (Role == "target" ? 300 : 100);
-			}
-			if (!string.IsNullOrEmpty(FilterStage))
-			{
-				priority += (Role == "target" ? 80 : 40);
-			}
-			priority += !string.IsNullOrEmpty(FilterTag) ? 10 : 0;
-			priority += !string.IsNullOrEmpty(Gender) ? 5 : 0;
-			priority += !string.IsNullOrEmpty(Status) ? 5 : 0;
-			return priority;
-		}
-
-		public bool HasAdvancedConditions
-		{
-			get
-			{
-				return !string.IsNullOrEmpty(Status) ||
-					!string.IsNullOrEmpty(Variable) ||
-					!string.IsNullOrEmpty(FilterId) ||
-					!string.IsNullOrEmpty(Role);
-			}
-		}
-	}
-
-	public class FilterRole : BasicRecord
-	{
-		public string Description;
-
-		public FilterRole(string id, string name, string description)
-		{
-			Key = id;
-			Name = name;
-			Description = description;
-		}
-	}
-
-	[Flags]
-	public enum TargetType
-	{
-		None = 0,
-		DirectTarget = 1,
-		Filter = 2,
-		All = 3,
-	}
-
-	public class ExpressionTest
-	{
-		[XmlAttribute("expr")]
-		public string Expression;
-
-		[DefaultValue("==")]
-		[XmlAttribute("cmp")]
-		public string Operator;
-
-		[DefaultValue("")]
-		[XmlAttribute("value")]
-		public string Value;
-
-		public ExpressionTest() { }
-
-		public ExpressionTest(string expr, string value)
-		{
-			Expression = expr;
-			Value = value;
-		}
-
-		public ExpressionTest(string serializedData)
-		{
-			string[] parts = serializedData.Split(new char[] { ':' });
-			if (parts.Length > 0)
-			{
-				Expression = parts[0];
-			}
-			if (parts.Length > 1)
-			{
-				Value = parts[1];
-			}
-			if (parts.Length > 2 && !string.IsNullOrEmpty(parts[2]))
-			{
-				Operator = parts[2];
-			}
-		}
-
-		public ExpressionTest Copy()
-		{
-			ExpressionTest copy = MemberwiseClone() as ExpressionTest;
-			return copy;
-		}
-
-		public string Serialize()
-		{
-			List<string> pieces = new List<string>();
-			pieces.Add(Expression);
-			pieces.Add(Value);
-			if (!string.IsNullOrEmpty(Operator) && Operator != "==")
-			{
-				pieces.Add(Operator);
 			}
-			return string.Join(":", pieces);
+			return list;
 		}
 
-		public override bool Equals(object obj)
-		{
-			ExpressionTest other = obj as ExpressionTest;
-			if (other == null) { return false; }
-			return Expression.Equals(other) && Value.Equals(other) && (Operator ?? "").Equals(other.Operator ?? "");
-		}
-
-		public override int GetHashCode()
-		{
-			int hash = Expression.GetHashCode();
-			hash = (hash * 397) ^ (Value ?? "").GetHashCode();
-			hash = (hash * 397) ^ (Operator ?? "").GetHashCode();
-			return hash;
-		}
-
-		public override string ToString()
+		public int GetSliceCount()
 		{
-			string op = Operator ?? "==";
-			return $"{Expression}{op}{Value}";
+			return Lines.Count;
 		}
 	}
 
diff --git a/editor source/SPNATI Character Editor/DataStructures/CaseLabel.cs b/editor source/SPNATI Character Editor/DataStructures/CaseLabel.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ab7595854a6766b46d8724c216edde62fcca2e8a
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataStructures/CaseLabel.cs	
@@ -0,0 +1,72 @@
+using Desktop.Skinning;
+using SPNATI_Character_Editor.Providers;
+using System;
+using System.Drawing;
+using System.Xml.Serialization;
+
+namespace SPNATI_Character_Editor
+{
+	public class CaseLabel
+	{
+		[XmlAttribute("id")]
+		public int Id;
+
+		[XmlAttribute("label")]
+		public string Text;
+
+		[XmlAttribute("color")]
+		public string ColorCode;
+
+		[XmlAttribute("folder")]
+		public string Folder;
+
+		public override string ToString()
+		{
+			return Text;
+		}
+	}
+
+	public class ColorCode : Definition
+	{
+		public int Code;
+		private Func<Skin, Color> _mapper;
+
+		public ColorCode()
+		{
+		}
+
+		public ColorCode(string name, int code, Func<Skin, Color> colorMapper)
+		{
+			Key = code.ToString();
+			Name = name;
+			Code = code;
+			_mapper = colorMapper;
+		}
+
+		public override string ToString()
+		{
+			return Name;
+		}
+
+		public Color GetColor()
+		{
+			if (_mapper == null)
+			{
+				return SkinManager.Instance.CurrentSkin.Surface.ForeColor;
+			}
+			return _mapper(SkinManager.Instance.CurrentSkin);
+		}
+	}
+
+	public class ColorCodeProvider : DefinitionProvider<ColorCode>
+	{
+		public override void ApplyDefaults(ColorCode definition)
+		{
+		}
+
+		public override string GetLookupCaption()
+		{
+			return "Choose a Color";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataStructures/Character.cs b/editor source/SPNATI Character Editor/DataStructures/Character.cs
index 840d6110a0611913c0ade48294684b66b955f081..b63939248a4b9afb68dc74ed5ac0ae0e93b0c741 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Character.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Character.cs	
@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.IO;
+using System.Xml;
 using System.Xml.Serialization;
 
 namespace SPNATI_Character_Editor
@@ -30,6 +31,9 @@ namespace SPNATI_Character_Editor
 		[XmlIgnore]
 		public string Group { get; }
 
+		[XmlIgnore]
+		public DateTime LastUpdate;
+
 		/// <summary>
 		/// Where did this character come from?
 		/// </summary>
@@ -92,6 +96,23 @@ namespace SPNATI_Character_Editor
 		[XmlArrayItem("tag")]
 		public List<CharacterTag> Tags;
 
+		[XmlElement("stylesheet")]
+		public string StyleSheetName;
+
+		private CharacterStyleSheet _styles;
+		[XmlIgnore]
+		public CharacterStyleSheet Styles
+		{
+			get
+			{
+				if (_styles == null && !string.IsNullOrEmpty(StyleSheetName))
+				{
+					_styles = CharacterStyleSheetSerializer.Load(this, StyleSheetName);
+				}
+				return _styles;
+			}
+		}
+
 		[XmlNewLine]
 		[XmlArray("start")]
 		[XmlArrayItem("state")]
@@ -111,6 +132,10 @@ namespace SPNATI_Character_Editor
 		[XmlElement("behaviour")]
 		public Behaviour Behavior = new Behaviour();
 
+		[XmlArray("nicknames")]
+		[XmlArrayItem("nickname")]
+		public List<Nickname> Nicknames = new List<Nickname>();
+
 		[XmlNewLine(XmlNewLinePosition.After)]
 		[XmlElement("epilogue")]
 		public List<Epilogue> Endings;
@@ -881,6 +906,9 @@ namespace SPNATI_Character_Editor
 		[XmlAttribute("to")]
 		public string To;
 
+		[XmlAnyElement]
+		public List<XmlElement> ExtraXml;
+
 		public CharacterTag() { }
 		public CharacterTag(string tag)
 		{
diff --git a/editor source/SPNATI Character Editor/DataStructures/CharacterEditorData.cs b/editor source/SPNATI Character Editor/DataStructures/CharacterEditorData.cs
index db52b327d8c42d0ccf0930afe44886ab718ff2eb..747e5f43a82f9d0759f49752f8634fd3612e2dbb 100644
--- a/editor source/SPNATI Character Editor/DataStructures/CharacterEditorData.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/CharacterEditorData.cs	
@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
+using System.Windows.Forms;
 using System.Xml.Serialization;
 
 namespace SPNATI_Character_Editor
@@ -43,6 +44,15 @@ namespace SPNATI_Character_Editor
 		public List<CaseNote> Notes = new List<CaseNote>();
 		private Dictionary<int, string> _notes = new Dictionary<int, string>();
 
+		[XmlArray("labels")]
+		[XmlArrayItem("label")]
+		public List<CaseLabel> Labels = new List<CaseLabel>();
+		private Dictionary<int, CaseLabel> _labels = new Dictionary<int, CaseLabel>();
+
+		[XmlArray("prefixes")]
+		[XmlArrayItem("prefix")]
+		public List<string> IgnoredPrefixes = new List<string>();
+
 		[XmlElement("nextId")]
 		/// <summary>
 		/// Next unique ID to assign
@@ -55,6 +65,21 @@ namespace SPNATI_Character_Editor
 		/// </summary>
 		public MarkerData Markers;
 
+		private HashSet<string> _usedFolders = new HashSet<string>();
+		[XmlIgnore]
+		public AutoCompleteStringCollection Folders
+		{
+			get
+			{
+				AutoCompleteStringCollection col = new AutoCompleteStringCollection();
+				foreach (string folder in _usedFolders)
+				{
+					col.Add(folder);
+				}
+				return col;
+			}
+		}
+
 		/// <summary>
 		/// Deferred initialization of things that aren't part of serialization and don't need to exist until the character's lines are being worked on
 		/// </summary>
@@ -92,6 +117,29 @@ namespace SPNATI_Character_Editor
 			_character.Behavior.CaseRemoved += Behavior_CaseRemoved;
 		}
 
+		/// <summary>
+		/// Copies instances of editor data on a copied case
+		/// </summary>
+		/// <param name="copiedCase"></param>
+		public void Copy(Case copiedCase)
+		{
+			if (copiedCase.Id > 0)
+			{
+				string note = GetNote(copiedCase);
+				CaseLabel label = GetLabel(copiedCase);
+				copiedCase.Id = 0;
+				AssignId(copiedCase);
+				if (!string.IsNullOrEmpty(note))
+				{
+					SetNote(copiedCase, note);
+				}
+				if (label != null)
+				{
+					SetLabel(copiedCase, label.Text, label.ColorCode, label.Folder);
+				}
+			}
+		}
+
 		private void Behavior_CaseRemoved(object sender, Case deletedCase)
 		{
 			//delete anything using this case
@@ -116,6 +164,7 @@ namespace SPNATI_Character_Editor
 				}
 
 				_notes.Remove(deletedCase.Id);
+				_labels.Remove(deletedCase.Id);
 			}
 		}
 
@@ -141,6 +190,12 @@ namespace SPNATI_Character_Editor
 				CaseNote note = new CaseNote() { Id = kvp.Key, Text = kvp.Value };
 				Notes.Add(note);
 			}
+
+			Labels.Clear();
+			foreach (KeyValuePair<int, CaseLabel> kvp in _labels)
+			{
+				Labels.Add(kvp.Value);
+			}
 		}
 
 		public void OnAfterDeserialize()
@@ -155,6 +210,15 @@ namespace SPNATI_Character_Editor
 				_notes[note.Id] = note.Text;
 			}
 
+			foreach (CaseLabel label in Labels)
+			{
+				_labels[label.Id] = label;
+				if (!string.IsNullOrEmpty(label.Folder))
+				{
+					_usedFolders.Add(label.Folder);
+				}
+			}
+
 			Markers?.OnAfterDeserialize();
 		}
 
@@ -278,6 +342,39 @@ namespace SPNATI_Character_Editor
 			_notes.TryGetValue(workingCase.Id, out text);
 			return text;
 		}
+
+		public void SetLabel(Case workingCase, string text, string colorCode, string folder)
+		{
+			if (string.IsNullOrEmpty(text) && (colorCode == null || colorCode == "0") && string.IsNullOrEmpty(folder))
+			{
+				_labels.Remove(workingCase.Id);
+				return;
+			}
+			AssignId(workingCase);
+			CaseLabel label = new CaseLabel()
+			{
+				Id = workingCase.Id,
+				ColorCode = colorCode,
+				Text = text,
+				Folder = folder,
+			};
+			_labels[workingCase.Id] = label;
+			if (!string.IsNullOrEmpty(folder))
+			{
+				_usedFolders.Add(folder);
+			}
+		}
+
+		public CaseLabel GetLabel(Case workingCase)
+		{
+			if (workingCase.Id == 0)
+			{
+				return null;
+			}
+			CaseLabel label = null;
+			_labels.TryGetValue(workingCase.Id, out label);
+			return label;
+		}
 	}
 
 	public class Situation
@@ -295,6 +392,13 @@ namespace SPNATI_Character_Editor
 		[DefaultValue(0)]
 		public int Id;
 
+		/// <summary>
+		/// Targeting priority
+		/// </summary>
+		[XmlAttribute("priority")]
+		[DefaultValue(SituationPriority.None)]
+		public SituationPriority Priority;
+
 		[XmlElement("trigger")]
 		public Case LegacyCase;
 
@@ -347,6 +451,14 @@ namespace SPNATI_Character_Editor
 		}
 	}
 
+	public enum SituationPriority
+	{
+		None = 0,
+		MustTarget = 1,
+		Noteworthy = 2,
+		FYI = 3
+	}
+
 	public class SituationResponse
 	{
 		[XmlAttribute("id")]
diff --git a/editor source/SPNATI Character Editor/DataStructures/CharacterStyleSheet.cs b/editor source/SPNATI Character Editor/DataStructures/CharacterStyleSheet.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a4687b15000a492b288e2d3382970e9781de181e
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataStructures/CharacterStyleSheet.cs	
@@ -0,0 +1,110 @@
+using Desktop.DataStructures;
+using SPNATI_Character_Editor.Controls.StyleControls;
+using SPNATI_Character_Editor.IO;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+namespace SPNATI_Character_Editor
+{
+	/// <summary>
+	/// CSS stylesheet that follows a very strict format
+	/// </summary>
+	public class CharacterStyleSheet : BindableObject
+	{
+		public string Name;
+
+		public ObservableCollection<StyleRule> Rules
+		{
+			get { return Get<ObservableCollection<StyleRule>>(); }
+			set { Set(value); }
+		}
+
+		public bool AdvancedMode
+		{
+			get { return Get<bool>(); }
+			set { Set(value); }
+		}
+
+		public string FullText
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		public CharacterStyleSheet()
+		{
+			Rules = new ObservableCollection<StyleRule>();
+		}
+	}
+
+	public class StyleRule : BindableObject
+	{
+		public string ClassName
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		public string Description
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[StyleAttributeEditControl(DisplayName = "Attribute")]
+		public ObservableCollection<StyleAttribute> Attributes
+		{
+			get { return Get<ObservableCollection<StyleAttribute>>(); }
+			set { Set(value); }
+		}
+
+		public StyleRule()
+		{
+			Attributes = new ObservableCollection<StyleAttribute>();
+		}
+
+		public string Serialize(string characterId)
+		{
+			List<string> output = new List<string>();
+			output.Add($".dialogue .{ClassName}[data-character=\"{characterId}\"] {{");
+			output.Add("  /*" + CharacterStyleSheetSerializer.CssEncode(Description) + "*/");
+			foreach (StyleAttribute attribute in Attributes)
+			{
+				output.Add($"  {attribute.Name}: {attribute.Value};");
+			}
+			output.Add("}");
+			return string.Join("\r\n", output);
+		}
+
+		public override string ToString()
+		{
+			return ClassName;
+		}
+	}
+
+	public class StyleAttribute : BindableObject
+	{
+		public string Name
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+		public string Value
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		public StyleAttribute() { }
+		public StyleAttribute(string attribute, string value)
+		{
+			Name = attribute;
+			Value = value;
+		}
+
+		public override string ToString()
+		{
+			return $"{Name}: {Value}";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataStructures/Choice.cs b/editor source/SPNATI Character Editor/DataStructures/Choice.cs
new file mode 100644
index 0000000000000000000000000000000000000000..df5c5c411e3fee335026581a93efb555732cf8e6
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataStructures/Choice.cs	
@@ -0,0 +1,44 @@
+using Desktop.CommonControls.PropertyControls;
+using System;
+using System.Xml.Serialization;
+
+namespace SPNATI_Character_Editor
+{
+	/// <summary>
+	/// User prompt choice
+	/// </summary>
+	public class Choice : ICloneable
+	{
+		[ComboBox(DisplayName = "Action", Key = "action", GroupOrder = 5, Description = "Action to perform", Options = new string[] { "jump", "marker" })]
+		[XmlAttribute("action")]
+		public string Action;
+
+		[Text(DisplayName = "ID", GroupOrder = 10, Key = "id", Description = "Action ID")]
+		[XmlAttribute("id")]
+		public string Id;
+
+		[Text(DisplayName = "Value", GroupOrder = 20, Key = "value", Description = "Action value")]
+		[XmlAttribute("value")]
+		public string Value;
+
+		[Text(DisplayName = "Caption", Key = "text", GroupOrder = 0, Description = "Button caption", RowHeight = 26, Multiline = true)]
+		[XmlText]
+		public string Caption;
+
+		/// <summary>
+		/// Parent directive
+		/// </summary>
+		[XmlIgnore]
+		public Directive Directive { get; set; }
+
+		public override string ToString()
+		{
+			return Caption;
+		}
+
+		public object Clone()
+		{
+			return MemberwiseClone();
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataStructures/ClothingDatabase.cs b/editor source/SPNATI Character Editor/DataStructures/ClothingDatabase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0d4d3c891d0a37f4169294546088b432e9eac1da
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataStructures/ClothingDatabase.cs	
@@ -0,0 +1,12 @@
+namespace SPNATI_Character_Editor
+{
+	public static class ClothingDatabase
+	{
+		public static CountedSet<string> Items { get; set; } = new CountedSet<string>();
+
+		public static void AddClothing(Clothing item)
+		{
+			Items.Add(item.GenericName);
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataStructures/Collectible.cs b/editor source/SPNATI Character Editor/DataStructures/Collectible.cs
index c627f497f6aa76d50210eacfdc29f3bd77a712c0..5a0e2e3fb986e469ffaeb1cca147ed94afac89fa 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Collectible.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Collectible.cs	
@@ -4,6 +4,7 @@ using SPNATI_Character_Editor.Controls;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Xml;
 using System.Xml.Serialization;
 
 namespace SPNATI_Character_Editor.DataStructures
@@ -55,11 +56,14 @@ namespace SPNATI_Character_Editor.DataStructures
 		[XmlElement("hide-details")]
 		public bool HideDetails;
 
-		[Numeric(DisplayName = "Counter", GroupOrder = 60, Description = "If checked, collectible will not appear in the collectibles list at all until unlocked", Minimum = 0, Maximum = 1000)]
+		[Numeric(DisplayName = "Counter", GroupOrder = 60, Description = "If checked, a progress bar will be displayed and the collectible will not be unlocked until reaching this value", Minimum = 0, Maximum = 1000)]
 		[DefaultValue(0)]
 		[XmlElement("counter")]
 		public int Counter;
 
+		[XmlAnyElement]
+		public List<XmlElement> ExtraXml;
+
 		[XmlIgnore]
 		public Character Character;
 
diff --git a/editor source/SPNATI Character Editor/DataStructures/CollectibleData.cs b/editor source/SPNATI Character Editor/DataStructures/CollectibleData.cs
index 7aab7a73b31caf765889896a12b1d15305ed6a0a..fd8e67a3bcc276761fd3c011a37cb18620177f95 100644
--- a/editor source/SPNATI Character Editor/DataStructures/CollectibleData.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/CollectibleData.cs	
@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using System.Xml;
 using System.Xml.Serialization;
 
 namespace SPNATI_Character_Editor.DataStructures
@@ -9,6 +10,9 @@ namespace SPNATI_Character_Editor.DataStructures
 		[XmlElement("collectible")]
 		public List<Collectible> Collectibles = new List<Collectible>();
 
+		[XmlAnyElement]
+		public List<XmlElement> ExtraXml;
+
 		public int Count
 		{
 			get { return Collectibles.Count; }
@@ -17,7 +21,6 @@ namespace SPNATI_Character_Editor.DataStructures
 		public void Add(Collectible collectible)
 		{
 			Collectibles.Add(collectible);
-			Collectibles.Sort();
 		}
 
 		public void Remove(Collectible collectible)
diff --git a/editor source/SPNATI Character Editor/DataStructures/Definition.cs b/editor source/SPNATI Character Editor/DataStructures/Definition.cs
index 66ebe574431f10d8265a87fd113e562ae2b1c6a0..6ef043b57261419b116ad06a4efac01b9336334b 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Definition.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Definition.cs	
@@ -1,12 +1,18 @@
 using Desktop;
+using Newtonsoft.Json;
 
 namespace SPNATI_Character_Editor
 {
+	[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
 	public class Definition : IRecord
 	{
+		[JsonProperty("name")]
 		public string Name { get; set; }
+		[JsonProperty("key")]
 		public string Key { get; set; }
+		[JsonProperty("group")]
 		public string Group { get; set; }
+		[JsonProperty("description")]
 		public string Description { get; set; }
 
 		public int CompareTo(IRecord other)
diff --git a/editor source/SPNATI Character Editor/DataStructures/DialogueNode.cs b/editor source/SPNATI Character Editor/DataStructures/DialogueNode.cs
index 1e063b75337049e639b205f31c9f1dc2c5593696..355efc5c7fb4ace2d0bc036a3cca1637f44a8f11 100644
--- a/editor source/SPNATI Character Editor/DataStructures/DialogueNode.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/DialogueNode.cs	
@@ -1,55 +1,242 @@
-using Desktop;
+using Desktop.CommonControls;
+using Desktop.DataStructures;
 using System;
+using System.Text;
 
 namespace SPNATI_Character_Editor
 {
 	/// <summary>
 	/// Helper class for tagging tree nodes to particular cases
 	/// </summary>
-	public class DialogueNode
+	public class DialogueNode : BindableObject, IGroupedItem, IComparable<DialogueNode>
 	{
 		public NodeType NodeType;
+		public NodeMode Mode;
+
+		public int Dummy
+		{
+			get { return Get<int>(); }
+			set { Set(value); }
+		}
 
 		public Character Character;
+		private CharacterEditorData _editorData;
 
 		public Stage Stage;
-		public Case Case;
+		public Case Case
+		{
+			get { return Get<Case>(); }
+			set { Set(value); }
+		}
 
-		public DialogueNode(Character character)
+		public string Tag
 		{
-			Character = character;
-			NodeType = NodeType.Start;
+			get
+			{
+				Trigger trigger = TriggerDatabase.GetTrigger(Case.Tag);
+				return trigger?.Label ?? Case.Tag;
+			}
 		}
 
-		public DialogueNode(Character character, Stage stage)
+		public string Label
 		{
-			Character = character;
-			NodeType = NodeType.Stage;
-			Stage = stage;
+			get
+			{
+				return $"{StageRange} - {Case.ToString()}";
+			}
+		}
+
+		public string CustomLabel
+		{
+			get
+			{
+				CaseLabel label = _editorData?.GetLabel(Case);
+				if (label != null && !string.IsNullOrEmpty(label.Text))
+				{
+					return label.Text;
+				}
+				return "";
+			}
+		}
+
+		public string Conditions
+		{
+			get
+			{
+				Case workingCase = Case;
+				string label = CustomLabel;
+				if (!string.IsNullOrEmpty(label))
+				{
+					return label;
+				}
+
+				string conditions = workingCase.ToConditionsString(true);
+				if (string.IsNullOrEmpty(conditions))
+				{
+					return "-";
+				}
+				return conditions;
+			}
+		}
+
+		public string StageRange
+		{
+			get
+			{
+				Case c = Case;
+				StringBuilder sb = new StringBuilder();
+				if (c.Stages.Count == 0)
+				{
+					sb.Append("???");
+				}
+				else
+				{
+					int last = c.Stages[0];
+					int startRange = last;
+					for (int i = 1; i < c.Stages.Count; i++)
+					{
+						int stage = c.Stages[i];
+						if (stage - 1 > last)
+						{
+							if (startRange == last)
+							{
+								sb.Append(startRange.ToString() + ",");
+							}
+							else
+							{
+								sb.Append($"{startRange}-{last},");
+							}
+							startRange = stage;
+						}
+						last = stage;
+					}
+					if (startRange == last)
+					{
+						sb.Append(startRange.ToString());
+					}
+					else
+					{
+						sb.Append($"{startRange}-{last}");
+					}
+				}
+				return sb.ToString();
+			}
+		}
+
+		public string Target
+		{
+			get
+			{
+				if (!string.IsNullOrEmpty(Case.Target))
+				{
+					return GetCharacterName(Case.Target);
+				}
+				if (!string.IsNullOrEmpty(Case.AlsoPlaying))
+				{
+					return GetCharacterName(Case.AlsoPlaying);
+				}
+				return "-";
+			}
+		}
+
+		private string GetCharacterName(string key)
+		{
+			Character c = CharacterDatabase.Get(key);
+			if (c == null)
+			{
+				return key;
+			}
+			return c.Label;
+		}
+
+		public string Priority
+		{
+			get
+			{
+				if (Case.Hidden == "1")
+				{
+					return "-";
+				}
+				else if (!string.IsNullOrEmpty(Case.CustomPriority))
+				{
+					return "*" + Case.CustomPriority;
+				}
+				return Case.GetPriority().ToString();
+			}
 		}
 
 		public DialogueNode(Character character, Stage stage, Case stageCase)
 		{
 			Character = character;
-			NodeType = NodeType.Case;
+			_editorData = CharacterDatabase.GetEditorData(character);
 			Stage = stage;
 			Case = stageCase;
 		}
 
 		public override string ToString()
 		{
-			switch (NodeType)
+			return string.Format("{0}", Case.ToString());
+		}
+
+		public string GetGroupKey()
+		{
+			string key = "";
+			if (Mode == NodeMode.Stage)
+			{
+				key = Stage.Id.ToString();
+			}
+			else
 			{
-				case NodeType.Start:
-					return "Starting Lines";
-				case NodeType.Stage:
-					return string.Format("Stage: {0} ({1})", Character.LayerToStageName(Stage.Id), Stage.Id);
-				case NodeType.Case:
-					return string.Format("{0}", Case.ToString());
-				default:
-					return "Unknown node";
+				key = Case.Tag;
 			}
+			if (_editorData != null && Case.Id > 0)
+			{
+				CaseLabel label = _editorData.GetLabel(Case);
+				if (label != null && !string.IsNullOrEmpty(label.Folder))
+				{
+					key += ">" + label.Folder;
+				}
+			}
+			return key;
+		}
+
+		public int CompareTo(DialogueNode other)
+		{
+			return Case.CompareTo(other.Case);
 		}
+
+		public static int CompareCases(DialogueNode caseNode1, DialogueNode caseNode2)
+		{
+			string tag1 = caseNode1.Case.Tag;
+			string tag2 = caseNode2.Case.Tag;
+			int diff = TriggerDatabase.Compare(tag1, tag2);
+
+			if (diff == 0)
+			{
+				int stage1 = caseNode1.Case.Stages.Count > 0 ? caseNode1.Case.Stages[0] : -1;
+				int stage2 = caseNode2.Case.Stages.Count > 0 ? caseNode2.Case.Stages[0] : -1;
+				diff = stage1.CompareTo(stage2);
+				if (diff == 0)
+				{
+					diff = caseNode1.Case.Stages[caseNode1.Case.Stages.Count - 1].CompareTo(caseNode2.Case.Stages[caseNode2.Case.Stages.Count - 1]);
+					if (diff == 0)
+					{
+						diff = caseNode2.Case.GetPriority().CompareTo(caseNode1.Case.GetPriority());
+						if (diff == 0)
+						{
+							diff = caseNode1.Label.CompareTo(caseNode2.Label);
+						}
+					}
+				}
+			}
+			return diff;
+		}
+	}
+
+	public enum NodeMode
+	{
+		Case,
+		Stage
 	}
 
 	public enum NodeType
diff --git a/editor source/SPNATI Character Editor/DataStructures/Directive.cs b/editor source/SPNATI Character Editor/DataStructures/Directive.cs
index 15930a6d26078f9ae45d95ee4e0974f1a5fe4e78..009d76ee5735a143fcca6165597f87b21853780d 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Directive.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Directive.cs	
@@ -19,6 +19,10 @@ namespace SPNATI_Character_Editor
 		[XmlAttribute("id")]
 		public string Id;
 
+		[DefaultValue("")]
+		[XmlAttribute("parent")]
+		public string ParentId;
+
 		[DirectiveMarker(DisplayName = "Marker", GroupOrder = 0, Key = "marker", Description = "Run this directive only if the marker's condition is met", ShowPrivate = true)]
 		[XmlAttribute("marker")]
 		public string Marker;
@@ -181,9 +185,16 @@ namespace SPNATI_Character_Editor
 		public bool IgnoreRotation;
 		#endregion
 
+		[Text(DisplayName = "Title", Key = "title", GroupOrder = -1, Description = "User prompt title text", RowHeight = 52, Multiline = true)]
+		[XmlElement("title")]
+		public string Title;
+
 		[XmlElement("keyframe")]
 		public List<Keyframe> Keyframes = new List<Keyframe>();
 
+		[XmlElement("choice")]
+		public List<Choice> Choices = new List<Choice>();
+
 		public Directive() { }
 
 		public Directive(string type)
@@ -252,6 +263,12 @@ namespace SPNATI_Character_Editor
 				case "emit":
 					text = $"Emit {Id} ({Count})";
 					break;
+				case "jump":
+					text = $"Jump to scene {Id}";
+					break;
+				case "prompt":
+					text = $"Prompt: {Title}";
+					break;
 			}
 
 			return $"{prefix}{text}";
@@ -280,6 +297,13 @@ namespace SPNATI_Character_Editor
 				clonedFrame.Directive = clone;
 				clone.Keyframes.Add(clonedFrame);
 			}
+			clone.Choices = new List<Choice>();
+			foreach (Choice c in Choices)
+			{
+				Choice clonedChoice = c.Clone() as Choice;
+				clonedChoice.Directive = clone;
+				clone.Choices.Add(clonedChoice);
+			}
 			return clone;
 		}
 
@@ -307,6 +331,9 @@ namespace SPNATI_Character_Editor
 			Scene scene = cxt.Scene;
 
 			HashSet<string> usedIds = new HashSet<string>();
+			usedIds.Add("camera");
+			usedIds.Add("fade");
+			usedIds.Add("background");
 
 			//make sure this is the first "add" directive with this ID
 			for (int i = 0; i < scene.Directives.Count; i++)
diff --git a/editor source/SPNATI Character Editor/DataStructures/ExpressionTest.cs b/editor source/SPNATI Character Editor/DataStructures/ExpressionTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..864db6c9450c83dd10bff89403f93f85da48a538
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataStructures/ExpressionTest.cs	
@@ -0,0 +1,108 @@
+using Desktop.DataStructures;
+using Newtonsoft.Json;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Xml.Serialization;
+
+namespace SPNATI_Character_Editor
+{
+	public class ExpressionTest : BindableObject
+	{
+		[XmlAttribute("expr")]
+		[JsonProperty("expr")]
+		public string Expression
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[DefaultValue("==")]
+		[XmlAttribute("cmp")]
+		[JsonProperty("cmp")]
+		public string Operator
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[DefaultValue("")]
+		[XmlAttribute("value")]
+		[JsonProperty("value")]
+		public string Value
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		public static readonly string[] Operators = new string[] { "==", "<=", "<", ">", ">=", "!=" };
+
+		public ExpressionTest() { }
+
+		public ExpressionTest(string expr, string value)
+		{
+			Expression = expr;
+			Value = value;
+		}
+
+		public ExpressionTest(string serializedData)
+		{
+			string[] parts = serializedData.Split(new char[] { ':' });
+			if (parts.Length > 0)
+			{
+				Expression = parts[0];
+			}
+			if (parts.Length > 1)
+			{
+				Value = parts[1];
+			}
+			if (parts.Length > 2 && !string.IsNullOrEmpty(parts[2]))
+			{
+				Operator = parts[2];
+			}
+		}
+
+		public ExpressionTest Copy()
+		{
+			ExpressionTest copy = new ExpressionTest();
+			CopyPropertiesInto(copy);
+			return copy;
+		}
+
+		public string Serialize()
+		{
+			List<string> pieces = new List<string>();
+			pieces.Add(Expression);
+			pieces.Add(Value);
+			if (!string.IsNullOrEmpty(Operator) && Operator != "==")
+			{
+				pieces.Add(Operator);
+			}
+			return string.Join(":", pieces);
+		}
+
+		public override bool Equals(object obj)
+		{
+			ExpressionTest other = obj as ExpressionTest;
+			if (other == null) { return false; }
+			return Expression.Equals(other.Expression) && (Value ?? "").Equals(other.Value ?? "") && (Operator ?? "").Equals(other.Operator ?? "");
+		}
+
+		public override int GetHashCode()
+		{
+			int hash = (Expression ?? "").GetHashCode();
+			hash = (hash * 397) ^ (Value ?? "").GetHashCode();
+			hash = (hash * 397) ^ (Operator ?? "").GetHashCode();
+			return hash;
+		}
+
+		public override string ToString()
+		{
+			if (string.IsNullOrEmpty(Value))
+			{
+				return Expression;
+			}
+			string op = Operator ?? "==";
+			return $"{Expression}{op}{Value}";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataStructures/Metadata.cs b/editor source/SPNATI Character Editor/DataStructures/Metadata.cs
index 373d03d98600adee3a81547440c9cb7fbf31543d..5aeec3faf1890dbc4ecd6b1781a671a1c5741feb 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Metadata.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Metadata.cs	
@@ -82,6 +82,23 @@ namespace SPNATI_Character_Editor
 		[XmlElement("poses")]
 		public int Poses;
 
+		/// <summary>
+		/// Custom z-ordering
+		/// </summary>
+		[DefaultValue(0)]
+		[XmlElement("z-index")]
+		public int Z;
+
+		/// <summary>
+		/// Speech bubble position relative to image
+		/// </summary>
+		[DefaultValue(DialogueLayer.over)]
+		[XmlElement("dialogue-layer")]
+		public DialogueLayer BubblePosition;
+
+		[XmlAnyElement]
+		public List<System.Xml.XmlElement> ExtraXml;
+
 		public Metadata()
 		{
 		}
@@ -161,4 +178,10 @@ namespace SPNATI_Character_Editor
 		[XmlText]
 		public string Title;
 	}
+
+	public enum DialogueLayer
+	{
+		over,
+		under
+	}
 }
diff --git a/editor source/SPNATI Character Editor/DataStructures/Nickname.cs b/editor source/SPNATI Character Editor/DataStructures/Nickname.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0addea73f5f4fa6348378f4d28b921c719842312
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataStructures/Nickname.cs	
@@ -0,0 +1,39 @@
+using System;
+using System.Xml.Serialization;
+
+namespace SPNATI_Character_Editor.DataStructures
+{
+	public class Nickname : IComparable<Nickname>
+	{
+		[XmlAttribute("for")]
+		public string Character;
+
+		[XmlText]
+		public string Label;
+
+		public Nickname()
+		{
+		}
+
+		public Nickname(string character, string label)
+		{
+			Character = character;
+			Label = label;
+		}
+
+		public int CompareTo(Nickname other)
+		{
+			int compare = Character.CompareTo(other.Character);
+			if (compare == 0)
+			{
+				compare = Label.CompareTo(other.Label);
+			}
+			return compare;
+		}
+
+		public override string ToString()
+		{
+			return $"{Character}: {Label}";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataStructures/Pose.cs b/editor source/SPNATI Character Editor/DataStructures/Pose.cs
index f20aec9d24901ac220d93818f22ec4ff8b219668..c35f0c4a5f6e15d3b758682d8b6faf714b4fc921 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Pose.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Pose.cs	
@@ -34,6 +34,10 @@ namespace SPNATI_Character_Editor
 
 		public override string ToString()
 		{
+			if (string.IsNullOrEmpty(Id))
+			{
+				return "???";
+			}
 			return Id;
 		}
 
@@ -102,9 +106,9 @@ namespace SPNATI_Character_Editor
 					sprite.Delay = item.Start.ToString(CultureInfo.InvariantCulture);
 				}
 
-				if (item.Keyframes.Count >= 0)
+				if (item.Keyframes.Count > 0)
 				{
-					LiveKeyframe initialFrame = item.Keyframes[0];
+					LiveSpriteKeyframe initialFrame = item.Keyframes[0] as LiveSpriteKeyframe;
 					if (!string.IsNullOrEmpty(initialFrame.Src))
 					{
 						sprite.Src = initialFrame.Src;
@@ -144,89 +148,37 @@ namespace SPNATI_Character_Editor
 
 					//2. split remainder of keyframes into animation directives of similar settings
 					DualKeyDictionary<float, string, PoseDirective> directives = new DualKeyDictionary<float, string, PoseDirective>();
-					foreach (string property in LiveKeyframe.TrackedProperties)
+					foreach (string property in initialFrame.TrackedProperties)
 					{
 						float delay = item.Start;
-						if (item.Keyframes.Count > 0)
+						bool foundFirst = false;
+						for (int i = 0; i < item.Keyframes.Count; i++)
 						{
-							for (int i = 0; i < item.Keyframes.Count; i++)
+							LiveSpriteKeyframe kf = item.Keyframes[i] as LiveSpriteKeyframe;
+							if (!kf.HasProperty(property)) { continue; }
+							if (!foundFirst)
+							{
+								foundFirst = true;
+								delay += kf.Time;
+							}
+							LiveKeyframe blockStart = item.GetBlockKeyframe(property, kf.Time);
+							LiveKeyframeMetadata metadata = blockStart.GetMetadata(property, false);
+							string metaKey = metadata.ToKey();
+							KeyframeType frameType = kf.GetFrameType(property);
+							if (frameType != KeyframeType.Normal)
 							{
-								LiveKeyframe kf = item.Keyframes[i];
-								if (kf.HasProperty(property))
+								if (frameType == KeyframeType.Split)
 								{
-									PoseDirective directive;
-
-									AnimatedProperty settings = item.GetAnimationProperties(property);
-									string settingsKey = settings.ToKey(kf.Time);
-									if (kf.InterpolationBreaks.ContainsKey(property))
-									{
-										//force a new directive
-										delay = item.Start + kf.Time;
-									}
-									directive = directives.Get(delay, settingsKey);
-									if (directive == null)
-									{
-										directive = new PoseDirective()
-										{
-											Id = item.Id,
-											DirectiveType = "animation",
-											Looped = settings.Looped,
-											EasingMethod = settings.Ease.GetValue(kf.Time),
-											InterpolationMethod = settings.Interpolation.GetValue(kf.Time),
-											Marker = item.Marker,
-										};
-										if (delay > 0)
-										{
-											directive.Delay = delay.ToString(CultureInfo.InvariantCulture);
-										}
-										Directives.Add(directive);
-										directives.Set(delay, settingsKey, directive);
-									}
-
-									string time = (kf.Time + item.Start - delay).ToString(CultureInfo.InvariantCulture);
-									Keyframe frame = directive.Keyframes.Find(k => k.Time == time);
-									if (frame == null)
-									{
-										frame = new Keyframe();
-										frame.Time = time;
-										directive.Keyframes.Add(frame);
-										directive.Keyframes.Sort((k1, k2) =>
-										{
-											return k1.Time.CompareTo(k2.Time);
-										});
-									}
-									switch (property)
-									{
-										case "Src":
-											frame.Src = kf.Src;
-											break;
-										case "X":
-											frame.X = kf.X.Value.ToString(CultureInfo.InvariantCulture);
-											break;
-										case "Y":
-											frame.Y = kf.Y.Value.ToString(CultureInfo.InvariantCulture);
-											break;
-										case "Alpha":
-											frame.Opacity = kf.Alpha.Value.ToString(CultureInfo.InvariantCulture);
-											break;
-										case "Rotation":
-											frame.Rotation = kf.Rotation.Value.ToString(CultureInfo.InvariantCulture);
-											break;
-										case "ScaleX":
-											frame.ScaleX = kf.ScaleX.Value.ToString(CultureInfo.InvariantCulture);
-											break;
-										case "ScaleY":
-											frame.ScaleY = kf.ScaleY.Value.ToString(CultureInfo.InvariantCulture);
-											break;
-										case "SkewX":
-											frame.SkewX = kf.SkewX.Value.ToString(CultureInfo.InvariantCulture);
-											break;
-										case "SkewY":
-											frame.SkewY = kf.SkewY.Value.ToString(CultureInfo.InvariantCulture);
-											break;
-									}
+									//for a split, we need a key frame to conclude this animation and use the same thing at the start of the next animation, so create an extra
+									LiveKeyframeMetadata previousMetadata = item.GetBlockMetadata(property, kf.Time - 0.001f);
+									string previousMetaKey = previousMetadata.ToKey();
+									CreateKeyFrame(item, directives, property, delay, kf, previousMetadata, previousMetaKey);
 								}
+
+								//force a new directive
+								delay = item.Start + kf.Time;
 							}
+							CreateKeyFrame(item, directives, property, delay, kf, metadata, metaKey);
 						}
 					}
 
@@ -238,34 +190,97 @@ namespace SPNATI_Character_Editor
 						{
 							Directives.RemoveAt(i);
 						}
+						else if (directive.Keyframes.Count > 1)
+						{
+							directive.Keyframes.Sort((k1, k2) =>
+							{
+								float t1;
+								float t2;
+								float.TryParse(k1.Time, NumberStyles.Number, CultureInfo.InvariantCulture, out t1);
+								float.TryParse(k2.Time, NumberStyles.Number, CultureInfo.InvariantCulture, out t2);
+								return t1.CompareTo(t2);
+							});
+						}
 					}
 				}
-				//if (!item.LinkedToEnd)
-				//{
-				//	//for finite-length sprites, add an anim-break opacity change at the very end
-				//	string delay = (item.Start + item.Length).ToString(CultureInfo.InvariantCulture);
+			}
+		}
 
-				//	//but first get rid of any directives that change opacity starting at that point
-				//	for (int i = 0; i < Directives.Count; i++)
-				//	{
-				//		Directive dir = Directives[i];
-				//		if (dir.Id == item.Id && dir.Delay == delay && dir.Opacity == "0")
-				//		{
-				//			Directives.RemoveAt(i);
-				//			break;
-				//		}
-				//	}
+		private void CreateKeyFrame(LiveSprite item, DualKeyDictionary<float, string, PoseDirective> directives, string property, float delay, LiveSpriteKeyframe kf, LiveKeyframeMetadata metadata, string metaKey)
+		{
+			PoseDirective directive = directives.Get(delay, metaKey);
+			if (directive == null)
+			{
+				directive = new PoseDirective()
+				{
+					Id = item.Id,
+					DirectiveType = "animation",
+					Looped = metadata.Looped,
+					Iterations = metadata.Iterations,
+					ClampingMethod = metadata.ClampMethod,
+					EasingMethod = metadata.Ease,
+					InterpolationMethod = metadata.Interpolation,
+					Marker = item.Marker,
+				};
+				if (delay > 0)
+				{
+					directive.Delay = delay.ToString(CultureInfo.InvariantCulture);
+				}
+				Directives.Add(directive);
+				directives.Set(delay, metaKey, directive);
+			}
 
-				//	//now add the opacity change
-				//	PoseDirective directive = new PoseDirective()
-				//	{
-				//		Id = item.Id,
-				//		DirectiveType = "animation",
-				//		Delay = delay
-				//	};
-				//	directive.Keyframes.Add(new Keyframe() { Time = "0", Opacity = "0" });
-				//	Directives.Add(directive);
-				//}
+			string time = (kf.Time + item.Start - delay).ToString(CultureInfo.InvariantCulture);
+			Keyframe frame = directive.Keyframes.Find(k => k.Time == time);
+			if (frame == null)
+			{
+				frame = new Keyframe();
+				frame.Time = time;
+				bool added = false;
+				for (int i = 0; i < directive.Keyframes.Count; i++)
+				{
+					Keyframe other = directive.Keyframes[i];
+					if (other.Time.CompareTo(time) > 0)
+					{
+						directive.Keyframes.Insert(i, frame);
+						added = true;
+						break;
+					}
+				}
+				if (!added)
+				{
+					directive.Keyframes.Add(frame);
+				}
+			}
+			switch (property)
+			{
+				case "Src":
+					frame.Src = kf.Src;
+					break;
+				case "X":
+					frame.X = kf.X.Value.ToString(CultureInfo.InvariantCulture);
+					break;
+				case "Y":
+					frame.Y = kf.Y.Value.ToString(CultureInfo.InvariantCulture);
+					break;
+				case "Alpha":
+					frame.Opacity = kf.Alpha.Value.ToString(CultureInfo.InvariantCulture);
+					break;
+				case "Rotation":
+					frame.Rotation = kf.Rotation.Value.ToString(CultureInfo.InvariantCulture);
+					break;
+				case "ScaleX":
+					frame.ScaleX = kf.ScaleX.Value.ToString(CultureInfo.InvariantCulture);
+					break;
+				case "ScaleY":
+					frame.ScaleY = kf.ScaleY.Value.ToString(CultureInfo.InvariantCulture);
+					break;
+				case "SkewX":
+					frame.SkewX = kf.SkewX.Value.ToString(CultureInfo.InvariantCulture);
+					break;
+				case "SkewY":
+					frame.SkewY = kf.SkewY.Value.ToString(CultureInfo.InvariantCulture);
+					break;
 			}
 		}
 
diff --git a/editor source/SPNATI Character Editor/DataStructures/Recipe.cs b/editor source/SPNATI Character Editor/DataStructures/Recipe.cs
new file mode 100644
index 0000000000000000000000000000000000000000..331e1c758e0ec02408c3523bc5cd14ed82533e60
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataStructures/Recipe.cs	
@@ -0,0 +1,150 @@
+using Desktop;
+using Newtonsoft.Json;
+using SPNATI_Character_Editor.Providers;
+using System;
+using System.IO;
+
+namespace SPNATI_Character_Editor
+{
+	/// <summary>
+	/// Recipe for creating a case geared to target a particular game situation
+	/// </summary>
+	[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+	public class Recipe : Definition
+	{
+		public bool Core;
+
+		[JsonProperty("case")]
+		public Case Case = new Case();
+
+		[JsonProperty("label")]
+		public string Label;
+
+		public string FileName;
+
+		public string GetFilePath()
+		{
+			string dir = "";
+			if (Core)
+			{
+				dir = Path.Combine("Resources", "Recipes");
+			}
+			else
+			{
+				dir = Path.Combine(Config.AppDataDirectory, "Recipes");
+				if (!Directory.Exists(dir))
+				{
+					Directory.CreateDirectory(dir);
+				}
+			}
+			string filename = FileName;
+			if (string.IsNullOrEmpty(filename))
+			{
+				filename = Key;
+			}
+			if (!filename.EndsWith(".txt"))
+			{
+				filename += ".txt";
+			}
+			return Path.Combine(dir, filename);
+		}
+
+		/// <summary>
+		/// Turn this recipe into a case for a character
+		/// </summary>
+		/// <param name="character"></param>
+		/// <returns></returns>
+		public Case AddToCharacter(Character character)
+		{
+			Case workingCase = Case.Copy();
+			//apply to all applicable stages by default
+			for (int stage = 0; stage < character.Layers + Clothing.ExtraStages; stage++)
+			{
+				if (TriggerDatabase.UsedInStage(workingCase.Tag, character, stage))
+				{
+					workingCase.Stages.Add(stage);
+				}
+				if (workingCase.TotalRounds == "1")
+				{
+					break;
+				}
+			}
+			if (workingCase.OneShotId > 0)
+			{
+				//need to assign a unique ID
+				workingCase.OneShotId = ++character.Behavior.MaxCaseId;
+			}
+			if (!string.IsNullOrEmpty(Label))
+			{
+				CharacterEditorData editorData = CharacterDatabase.GetEditorData(character);
+				if (editorData != null)
+				{
+					editorData.SetLabel(workingCase, Label, null, null);
+				}
+			}
+			DialogueLine line = new DialogueLine("happy", Description);
+			workingCase.Lines.Add(line);
+			character.Behavior.AddWorkingCase(workingCase);
+			return workingCase;
+		}
+	}
+
+	public class RecipeProvider : DefinitionProvider<Recipe>
+	{
+		protected override Recipe CreateRecord(string key)
+		{
+			string guid = Guid.NewGuid().ToString();
+			Recipe recipe = base.CreateRecord(guid);
+			recipe.Name = key;
+			return recipe;
+		}
+
+		public override string GetLookupCaption()
+		{
+			return "Choose a Recipe";
+		}
+
+		public override void ApplyDefaults(Recipe definition)
+		{
+			Recipe recipe = definition as Recipe;
+			recipe.Case.Tag = "hand";
+		}
+
+		public static void Load()
+		{
+			string dir = Path.Combine("Resources", "Recipes");
+			if (Directory.Exists(dir))
+			{
+				foreach (string file in Directory.EnumerateFiles(dir, "*.txt"))
+				{
+					try
+					{
+						string json = File.ReadAllText(file);
+						Recipe recipe = Json.Deserialize<Recipe>(json);
+						recipe.FileName = Path.GetFileName(file);
+						recipe.Core = true;
+						Definitions.Instance.Add(recipe);
+					}
+					catch { }
+				}
+			}
+
+			dir = Path.Combine(Config.AppDataDirectory, "Recipes");
+			if (Directory.Exists(dir))
+			{
+				foreach (string file in Directory.EnumerateFiles(dir, "*.txt"))
+				{
+					try
+					{
+						string json = File.ReadAllText(file);
+						Recipe recipe = Json.Deserialize<Recipe>(json);
+						recipe.FileName = Path.GetFileName(file);
+						Definitions.Instance.Add(recipe);
+					}
+					catch { }
+				}
+			}
+
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataStructures/Scene.cs b/editor source/SPNATI Character Editor/DataStructures/Scene.cs
index 15c618f77da763fbfc4440380c18a30f9d86fee7..d25ad59abdefe695786061f98752a8c344da1472 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Scene.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Scene.cs	
@@ -14,6 +14,10 @@ namespace SPNATI_Character_Editor
 		[XmlAttribute("transition")]
 		public bool Transition;
 
+		[Text(DisplayName = "ID", GroupOrder = -2, Description = "Scene ID")]
+		[XmlAttribute("id")]
+		public string Id;
+
 		[Text(DisplayName = "Name", GroupOrder = -1, Description = "Scene name")]
 		[XmlAttribute("name")]
 		public string Name;
diff --git a/editor source/SPNATI Character Editor/DataStructures/Skin.cs b/editor source/SPNATI Character Editor/DataStructures/Skin.cs
index ef7f381413a5686ef6af666c7e61dc2c93514368..5d3287d8707fcd3205d86572208333ae78e7e817 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Skin.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Skin.cs	
@@ -4,6 +4,7 @@ using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.IO;
+using System.Xml;
 using System.Xml.Serialization;
 
 namespace SPNATI_Character_Editor
@@ -12,6 +13,9 @@ namespace SPNATI_Character_Editor
 	{
 		[XmlElement("costume")]
 		public List<SkinLink> Skins = new List<SkinLink>();
+
+		[XmlAnyElement]
+		public List<XmlElement> ExtraXml;
 	}
 
 	[XmlRoot("costume", Namespace = "")]
@@ -60,6 +64,9 @@ namespace SPNATI_Character_Editor
 		[XmlArrayItem("pose")]
 		public List<Pose> Poses = new List<Pose>();
 
+		[XmlAnyElement]
+		public List<XmlElement> ExtraXml;
+
 		[XmlIgnore]
 		public Character Character { get; set; }
 
diff --git a/editor source/SPNATI Character Editor/DataStructures/Sprite.cs b/editor source/SPNATI Character Editor/DataStructures/Sprite.cs
index 2ec27d3b6cb869fc2d6fb37ad34542bf7136f2c1..c158211e0f356ae6d6a694fe8bcff63d556ce912 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Sprite.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Sprite.cs	
@@ -1,17 +1,10 @@
-using System.ComponentModel;
-using System.Xml.Serialization;
-
-namespace SPNATI_Character_Editor
+namespace SPNATI_Character_Editor
 {
 	/// <summary>
 	/// In-game (non-epilogue) sprite
 	/// </summary>
 	public class Sprite : Directive
 	{
-		[DefaultValue("")]
-		[XmlAttribute("parent")]
-		public string ParentId;
-
 		public override string ToString()
 		{
 			return $"Sprite: {Id}";
diff --git a/editor source/SPNATI Character Editor/DataStructures/Tag.cs b/editor source/SPNATI Character Editor/DataStructures/Tag.cs
index e63e9b3fdea3062fe2abfb0536ee50d25d457d22..0af5ad1544a8ea327fd9e6d76c698749e618ec10 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Tag.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Tag.cs	
@@ -1,11 +1,13 @@
 using Desktop;
+using Desktop.CommonControls;
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Xml.Serialization;
 
 namespace SPNATI_Character_Editor
 {
-	public class Tag : IRecord, IComparable<Tag>
+	public class Tag : IRecord, IComparable<Tag>, IGroupedItem, INotifyPropertyChanged
 	{
 		[XmlIgnore]
 		public string Name { get { return DisplayName; } }
@@ -38,6 +40,12 @@ namespace SPNATI_Character_Editor
 		[XmlIgnore]
 		public int Count;
 
+		public event PropertyChangedEventHandler PropertyChanged
+		{
+			add { }
+			remove { }
+		}
+
 		public string ToLookupString()
 		{
 			return Value;
@@ -45,7 +53,8 @@ namespace SPNATI_Character_Editor
 
 		public override string ToString()
 		{
-			return string.Format("{0} ({1})", DisplayName ?? Value, Count);
+			//return string.Format("{0} ({1})", DisplayName ?? Value, Count);
+			return DisplayName ?? Value;
 		}
 
 		public int CompareTo(Tag other)
@@ -57,5 +66,10 @@ namespace SPNATI_Character_Editor
 		{
 			return Name.CompareTo(other.Name);
 		}
+
+		public string GetGroupKey()
+		{
+			return Group ?? "Misc";
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/DataStructures/TargetCondition.cs b/editor source/SPNATI Character Editor/DataStructures/TargetCondition.cs
new file mode 100644
index 0000000000000000000000000000000000000000..00fb027104d3da9a5c9ea3550736905ad93e9e99
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataStructures/TargetCondition.cs	
@@ -0,0 +1,671 @@
+using Desktop;
+using Desktop.CommonControls.PropertyControls;
+using Desktop.DataStructures;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Xml.Serialization;
+
+namespace SPNATI_Character_Editor
+{
+	public class TargetCondition : BindableObject
+	{
+		[XmlAttribute("count")]
+		[JsonProperty("count")]
+		/// <summary>
+		/// Number of characters needing the filter tag
+		/// </summary>
+		public string Count
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[XmlAttribute("filter")]
+		[JsonProperty("filter")]
+		/// <summary>
+		/// Tag to condition on
+		/// </summary>
+		public string FilterTag
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[JsonProperty("gender")]
+		[XmlAttribute("gender")]
+		public string Gender
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[Status(DisplayName = "Status", Description = "Status of the characters to match", GroupOrder = 10, Required = true)]
+		[XmlAttribute("status")]
+		[JsonProperty("status")]
+		public string Status
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[DefaultValue("")]
+		[RecordSelect(DisplayName = "Role", GroupOrder = 20, Description = "What type of characters to target", RecordType = typeof(FilterRole), Required = true)]
+		[XmlAttribute("role")]
+		[JsonProperty("role")]
+		public string Role
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[DefaultValue("")]
+		[RecordSelect(DisplayName = "Character", GroupOrder = 30, Description = "Character to target", RecordType = typeof(Character), Required = true)]
+		[XmlAttribute("character")]
+		[JsonProperty("character")]
+		public string FilterId
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[DefaultValue("")]
+		[StageSelect(DisplayName = "Stage", GroupName = "Add Filter", GroupOrder = 40, Description = "Stage to target", BoundProperties = new string[] { "FilterId" })]
+		[XmlAttribute("stage")]
+		[JsonProperty("stage")]
+		public string FilterStage
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[DefaultValue("")]
+		[Text(DisplayName = "Store into variable", GroupOrder = 0, Description = "Name of variable to store a target that this condition matches", Required = true, Formatter = "FormatVariable")]
+		[XmlAttribute("var")]
+		[JsonProperty("var")]
+		public string Variable
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		//[NumericRange(DisplayName = "Layers", GroupName = "Add Filter", GroupOrder = 50, Description = "Number of layers the target has left")]
+		[DefaultValue("")]
+		[XmlAttribute("layers")]
+		[JsonProperty("layers")]
+		public string Layers
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		//[NumericRange(DisplayName = "Starting Layers", GroupName = "Add Filter", GroupOrder = 55, Description = "Number of layers the target started with")]
+		[DefaultValue("")]
+		[XmlAttribute("startingLayers")]
+		[JsonProperty("startingLayers")]
+		public string StartingLayers
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		//[NumericRange(DisplayName = "Time in Stage", GroupName = "Add Filter", GroupOrder = 60, Description = "Number of rounds since the last time this target lost a hand")]
+		[DefaultValue("")]
+		[XmlAttribute("timeInStage")]
+		[JsonProperty("timeInStage")]
+		public string TimeInStage
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		//[ComboBox(DisplayName = "Has Hand", GroupName = "Add Filter", GroupOrder = 65, Description = "Character has a particular poker hand",
+		//			Options = new string[] { "Nothing", "High Card", "One Pair", "Two Pair", "Three of a Kind", "Straight", "Flush", "Full House", "Four of a Kind", "Straight Flush", "Royal Flush" })]
+		[DefaultValue("")]
+		[XmlAttribute("hasHand")]
+		[JsonProperty("hasHand")]
+		public string Hand
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		//[NumericRange(DisplayName = "Consecutive Losses", GroupName = "Add Filter", GroupOrder = 63, Description = "Number of hands the target has lost in a row")]
+		[DefaultValue("")]
+		[XmlAttribute("consecutiveLosses")]
+		[JsonProperty("consecutiveLosses")]
+		public string ConsecutiveLosses
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		//[MarkerCondition(DisplayName = "Said Marker", GroupName = "Add Filter", GroupOrder = 45, Description = "Character has said a marker", ShowPrivate = false, BoundProperties = new string[] { "FilterId" })]
+		[DefaultValue("")]
+		[XmlAttribute("saidMarker")]
+		[JsonProperty("saidMarker")]
+		public string SaidMarker
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		//[MarkerCondition(DisplayName = "Saying Marker", GroupName = "Add Filter", GroupOrder = 46, Description = "Character is saying a marker", ShowPrivate = false, BoundProperties = new string[] { "FilterId" })]
+		[DefaultValue("")]
+		[XmlAttribute("sayingMarker")]
+		[JsonProperty("sayingMarker")]
+		public string SayingMarker
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		//[Marker(DisplayName = "Not Said Marker", GroupName = "Add Filter", GroupOrder = 47, Description = "Character has not said a marker", ShowPrivate = false, BoundProperties = new string[] { "FilterId" })]
+		[DefaultValue("")]
+		[XmlAttribute("notSaidMarker")]
+		[JsonProperty("notSaidMarker")]
+		public string NotSaidMarker
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		//[Text(DisplayName = "Saying Text", GroupName = "Add Filter", GroupOrder = 48, Description = "Character is saying some text at this very moment")]
+		[DefaultValue("")]
+		[XmlAttribute("saying")]
+		[JsonProperty("saying")]
+		public string Saying
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		public static readonly KeyValuePair<string, string>[] StatusTypes = {
+			new KeyValuePair<string, string>(null, ""),
+			new KeyValuePair<string, string>("lost_some", "Lost something"),
+			new KeyValuePair<string, string>("mostly_clothed", "Lost only accessories"),
+			new KeyValuePair<string, string>("decent", "Still covered by major articles"),
+			new KeyValuePair<string, string>("exposed", "Chest and/or crotch visible"),
+			new KeyValuePair<string, string>("chest_visible", "Chest visible"),
+			new KeyValuePair<string, string>("crotch_visible", "Crotch visible"),
+			new KeyValuePair<string, string>("topless", "Topless (not naked)"),
+			new KeyValuePair<string, string>("bottomless", "Bottomless (not naked)"),
+			new KeyValuePair<string, string>("naked", "Naked (fully exposed)"),
+			new KeyValuePair<string, string>("lost_all", "Lost all layers"),
+			new KeyValuePair<string, string>("alive", "Still in the game"),
+			new KeyValuePair<string, string>("masturbating", "Masturbating"),
+			new KeyValuePair<string, string>("finished", "Finished masturbating")
+		};
+
+		public TargetCondition()
+		{
+		}
+
+		public TargetCondition(string tag, string gender, string status, string count)
+		{
+			FilterTag = tag;
+			Gender = gender;
+			Status = status;
+			Count = count;
+		}
+
+		public TargetCondition(string serializedData, string count)
+		{
+			Count = count;
+
+			string[] parts = serializedData.Split('&');
+			foreach (string part in parts)
+			{
+				if (part.Contains(";"))
+				{
+					string[] pieces = part.Split(new char[] { ';' }, 2);
+					if (pieces.Length == 2)
+					{
+						string key = pieces[0];
+						string value = pieces[1];
+						switch (key)
+						{
+							case "var":
+								Variable = value;
+								break;
+							case "stage":
+								FilterStage = value;
+								break;
+							case "character":
+								FilterId = value;
+								break;
+							case "role":
+								Role = value;
+								break;
+							case "saying":
+								Saying = value;
+								break;
+							case "sayingmarker":
+								SayingMarker = value;
+								break;
+							case "saidmarker":
+								SaidMarker = value;
+								break;
+							case "notsaidmarker":
+								NotSaidMarker = value;
+								break;
+							case "timeinstage":
+								TimeInStage = value;
+								break;
+							case "losses":
+								ConsecutiveLosses = value;
+								break;
+							case "layers":
+								Layers = value;
+								break;
+							case "startinglayers":
+								StartingLayers = value;
+								break;
+							case "hashand":
+								Hand = value;
+								break;
+						}
+					}
+				}
+				else
+				{
+					if (part == "male" || part == "female")
+					{
+						Gender = part;
+					}
+					else if (part != "" && Array.Exists(StatusTypes, t => t.Key == part || "not_" + t.Key == part))
+					{
+						Status = part;
+					}
+					else
+					{
+						FilterTag = part;
+					}
+				}
+			}
+		}
+
+		public override bool Equals(object obj)
+		{
+			TargetCondition other = obj as TargetCondition;
+			if (other == null)
+			{
+				return false;
+			}
+			return FilterTag == other.FilterTag &&
+				Count == other.Count &&
+				Status == other.Status &&
+				Gender == other.Gender &&
+				Hand == other.Hand &&
+				Role == other.Role &&
+				FilterId == other.FilterId &&
+				FilterStage == other.FilterStage &&
+				TimeInStage == other.TimeInStage &&
+				Layers == other.Layers &&
+				StartingLayers == other.StartingLayers &&
+				SaidMarker == other.SaidMarker &&
+				NotSaidMarker == other.NotSaidMarker &&
+				SayingMarker == other.SayingMarker &&
+				Saying == other.Saying &&
+				ConsecutiveLosses == other.ConsecutiveLosses &&
+				Variable == other.Variable;
+		}
+
+		public override int GetHashCode()
+		{
+			int hash = (FilterTag ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (Gender ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (Status ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (Count ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (Hand ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (Role ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (FilterId ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (FilterStage ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (TimeInStage ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (Layers ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (StartingLayers ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (SaidMarker ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (NotSaidMarker ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (SayingMarker ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (Saying ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (ConsecutiveLosses ?? string.Empty).GetHashCode();
+			hash = (hash * 397) ^ (Variable ?? string.Empty).GetHashCode();
+			return hash;
+		}
+
+		public void ClearEmptyValues()
+		{
+			if (FilterTag == "")
+				FilterTag = null;
+			if (Gender == "")
+				Gender = null;
+			if (Status == "")
+				Status = null;
+		}
+
+		public TargetCondition Copy()
+		{
+			TargetCondition copy = new TargetCondition();
+			CopyPropertiesInto(copy);
+			return copy;
+		}
+
+		public string Serialize()
+		{
+			List<string> parts = new List<string>();
+			if (!string.IsNullOrEmpty(Status))
+			{
+				parts.Add(Status);
+			}
+			if (!string.IsNullOrEmpty(Gender))
+			{
+				parts.Add(Gender);
+			}
+			if (!string.IsNullOrEmpty(FilterTag))
+			{
+				parts.Add(FilterTag);
+			}
+			if (!string.IsNullOrEmpty(Role))
+			{
+				parts.Add("role;" + Role);
+			}
+			if (!string.IsNullOrEmpty(FilterId))
+			{
+				parts.Add("character;" + FilterId);
+			}
+			if (!string.IsNullOrEmpty(FilterStage))
+			{
+				parts.Add("stage;" + FilterStage);
+			}
+			if (!string.IsNullOrEmpty(Variable))
+			{
+				parts.Add("var;" + FilterId);
+			}
+			if (!string.IsNullOrEmpty(Layers))
+			{
+				parts.Add("layers;" + Layers);
+			}
+			if (!string.IsNullOrEmpty(StartingLayers))
+			{
+				parts.Add("startinglayers;" + StartingLayers);
+			}
+			if (!string.IsNullOrEmpty(TimeInStage))
+			{
+				parts.Add("timeinstage;" + TimeInStage);
+			}
+			if (!string.IsNullOrEmpty(ConsecutiveLosses))
+			{
+				parts.Add("losses;" + ConsecutiveLosses);
+			}
+			if (!string.IsNullOrEmpty(Hand))
+			{
+				parts.Add("hashand;" + Hand);
+			}
+			if (!string.IsNullOrEmpty(SaidMarker))
+			{
+				parts.Add("saidmarker;" + SaidMarker);
+			}
+			if (!string.IsNullOrEmpty(NotSaidMarker))
+			{
+				parts.Add("notsaidmarker;" + NotSaidMarker);
+			}
+			if (!string.IsNullOrEmpty(SayingMarker))
+			{
+				parts.Add("sayingmarker;" + SayingMarker);
+			}
+			if (!string.IsNullOrEmpty(Saying))
+			{
+				parts.Add("saying;" + Saying);
+			}
+			string data = string.Format("count-{1}:{0}", Count, string.Join("&", parts));
+			return data;
+		}
+
+		public override string ToString()
+		{
+			string str = GUIHelper.RangeToString(Count);
+			if (FilterTag == null && Status == null && Gender == null && !HasAdvancedConditions)
+			{
+				str += " players";
+			}
+			else
+			{
+				if (!string.IsNullOrEmpty(Role))
+				{
+					switch (Role)
+					{
+						case "target":
+							str += " target";
+							break;
+						case "opp":
+							str += " opposing";
+							break;
+						case "other":
+							str += " other";
+							break;
+					}
+				}
+				if (Status != null)
+				{
+					str += " " + Status.Replace("_", " ");
+				}
+				if (!string.IsNullOrEmpty(Gender))
+				{
+					str += " " + Gender + (FilterTag != null ? "" : "s");
+				}
+				if (FilterTag != null)
+				{
+					str += " " + FilterTag;
+				}
+				if (!string.IsNullOrEmpty(FilterId))
+				{
+					str += $" id: {FilterId}";
+				}
+				if (!string.IsNullOrEmpty(FilterStage))
+				{
+					str += $" stage: {FilterStage}";
+				}
+				if (!string.IsNullOrEmpty(Layers))
+				{
+					str += $" layers: {Layers}";
+				}
+				if (!string.IsNullOrEmpty(StartingLayers))
+				{
+					str += $" starting layers: {StartingLayers}";
+				}
+				if (!string.IsNullOrEmpty(TimeInStage))
+				{
+					str += $" time in stage: {TimeInStage}";
+				}
+				if (!string.IsNullOrEmpty(ConsecutiveLosses))
+				{
+					str += $" losses in row: {ConsecutiveLosses}";
+				}
+				if (!string.IsNullOrEmpty(SaidMarker))
+				{
+					str += $" said marker: {SaidMarker}";
+				}
+				if (!string.IsNullOrEmpty(NotSaidMarker))
+				{
+					str += $" not said marker: {NotSaidMarker}";
+				}
+				if (!string.IsNullOrEmpty(SayingMarker))
+				{
+					str += $" saying marker: {SayingMarker}";
+				}
+				if (!string.IsNullOrEmpty(Saying))
+				{
+					str += $" saying text: {Saying}";
+				}
+				if (!string.IsNullOrEmpty(Hand))
+				{
+					str += $" hand {Hand}";
+				}
+				if (!string.IsNullOrEmpty(Variable))
+				{
+					str += $" => {Variable}";
+				}
+			}
+			return str;
+		}
+
+		public int GetPriority()
+		{
+			int priority = 0;
+			if (Role == "target")
+			{
+				if (!string.IsNullOrEmpty(FilterId))
+				{
+					priority += 300;
+				}
+				if (!string.IsNullOrEmpty(FilterTag))
+				{
+					priority += 150;
+				}
+				if (!string.IsNullOrEmpty(FilterStage))
+				{
+					priority += 80;
+				}
+				if (!string.IsNullOrEmpty(Status))
+				{
+					priority += 70;
+				}
+				if (!string.IsNullOrEmpty(Layers))
+				{
+					priority += 40;
+				}
+				if (!string.IsNullOrEmpty(StartingLayers))
+				{
+					priority += 40;
+				}
+				if (!string.IsNullOrEmpty(ConsecutiveLosses))
+				{
+					priority += 60;
+				}
+				if (!string.IsNullOrEmpty(TimeInStage))
+				{
+					priority += 25;
+				}
+				if (!string.IsNullOrEmpty(Hand))
+				{
+					priority += 30;
+				}
+				if (!string.IsNullOrEmpty(Gender))
+				{
+					priority += 5;
+				}
+			}
+			else
+			{
+				if (!string.IsNullOrEmpty(FilterId))
+				{
+					priority += 100;
+				}
+				if (!string.IsNullOrEmpty(FilterTag))
+				{
+					priority += 10;
+				}
+				if (!string.IsNullOrEmpty(FilterStage))
+				{
+					priority += 40;
+				}
+				if (!string.IsNullOrEmpty(Status))
+				{
+					priority += 5;
+				}
+				if (!string.IsNullOrEmpty(Layers))
+				{
+					priority += 20;
+				}
+				if (!string.IsNullOrEmpty(StartingLayers))
+				{
+					priority += 20;
+				}
+				if (!string.IsNullOrEmpty(ConsecutiveLosses))
+				{
+					priority += 30;
+				}
+				if (!string.IsNullOrEmpty(TimeInStage))
+				{
+					priority += 15;
+				}
+				if (!string.IsNullOrEmpty(Hand))
+				{
+					priority += 15;
+				}
+				if (!string.IsNullOrEmpty(Gender))
+				{
+					priority += 5;
+				}
+			}
+			if (!string.IsNullOrEmpty(SaidMarker))
+			{
+				priority += 1;
+			}
+			if (!string.IsNullOrEmpty(NotSaidMarker))
+			{
+				priority += 1;
+			}
+			if (!string.IsNullOrEmpty(SayingMarker))
+			{
+				priority += 1;
+			}
+			if (!string.IsNullOrEmpty(Saying))
+			{
+				priority += 1;
+			}
+
+			return priority;
+		}
+
+		public bool HasAdvancedConditions
+		{
+			get
+			{
+				return !string.IsNullOrEmpty(Status) ||
+					!string.IsNullOrEmpty(Variable) ||
+					!string.IsNullOrEmpty(FilterId) ||
+					!string.IsNullOrEmpty(Role) ||
+					!string.IsNullOrEmpty(FilterStage) ||
+					!string.IsNullOrEmpty(TimeInStage) ||
+					!string.IsNullOrEmpty(ConsecutiveLosses) ||
+					!string.IsNullOrEmpty(SaidMarker) ||
+					!string.IsNullOrEmpty(SayingMarker) ||
+					!string.IsNullOrEmpty(NotSaidMarker) ||
+					!string.IsNullOrEmpty(Saying) ||
+					!string.IsNullOrEmpty(Hand) ||
+					!string.IsNullOrEmpty(Layers) ||
+					!string.IsNullOrEmpty(StartingLayers);
+			}
+		}
+
+		public string FormatVariable(string variable)
+		{
+			variable = variable.Replace("~", "");
+			variable = variable.Trim();
+			return variable;
+		}
+	}
+
+	public class FilterRole : BasicRecord
+	{
+		public string Description;
+
+		public FilterRole(string id, string name, string description)
+		{
+			Key = id;
+			Name = name;
+			Description = description;
+		}
+	}
+
+	[Flags]
+	public enum TargetType
+	{
+		None = 0,
+		DirectTarget = 1,
+		Filter = 2,
+		All = 3,
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataStructures/TargetId.cs b/editor source/SPNATI Character Editor/DataStructures/TargetId.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b53cf17c4a00d603b06d7bbfb2102ce0c60edac9
--- /dev/null
+++ b/editor source/SPNATI Character Editor/DataStructures/TargetId.cs	
@@ -0,0 +1,30 @@
+using Desktop;
+
+namespace SPNATI_Character_Editor
+{
+	public class TargetId : IRecord
+	{
+		public string Group { get; set; }
+		public string Key { get; set; }
+		public string Name { get; set; }
+		public string Description { get; set; }
+
+		public TargetId(string key, string name, string group, string description)
+		{
+			Group = group;
+			Key = key;
+			Name = name;
+			Description = description;
+		}
+
+		public int CompareTo(IRecord other)
+		{
+			return Name.CompareTo(other.Name);
+		}
+
+		public string ToLookupString()
+		{
+			return Name;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/DataStructures/Trigger.cs b/editor source/SPNATI Character Editor/DataStructures/Trigger.cs
index 56284dd78bcc7e629fe7f7e3c9736a738437afaf..2c43fa3a7eba9f5620dd4af1a420391394c272d3 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Trigger.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Trigger.cs	
@@ -1,13 +1,16 @@
-using System;
+using Desktop;
+using System;
 using System.Collections.Generic;
+using System.Drawing;
 using System.Xml.Serialization;
+using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor
 {
 	/// <summary>
 	/// Case tag metadata
 	/// </summary>
-	public class Trigger
+	public class Trigger : IRecord
 	{
 		public const string StartTrigger = "-";
 
@@ -44,6 +47,13 @@ namespace SPNATI_Character_Editor
 		[XmlAttribute("hasTarget")]
 		public bool HasTarget;
 
+		[XmlAttribute("color")]
+		public int ColorScheme
+		{
+			get;
+			set;
+		}
+
 		/// <summary>
 		/// Generic trigger that satisfies this as a default
 		/// </summary>
@@ -76,6 +86,22 @@ namespace SPNATI_Character_Editor
 		[XmlIgnore]
 		public bool Unrecognized { get; set; }
 
+		public string Name
+		{
+			get { return Label; }
+		}
+
+		public string Key
+		{
+			get { return Tag; }
+			set { Tag = value; }
+		}
+
+		string IRecord.Group
+		{
+			get { return ""; }
+		}
+
 		/// <summary>
 		/// Used by the editor to group tags that correspond to the same phase (ex. must_strip_normal and must_strip_winning)
 		/// </summary>
@@ -128,6 +154,16 @@ namespace SPNATI_Character_Editor
 		{
 			return Label;
 		}
+
+		public string ToLookupString()
+		{
+			return Label;
+		}
+
+		public int CompareTo(IRecord other)
+		{
+			return TriggerDatabase.Compare(Key, other.Key);
+		}
 	}
 
 	[XmlRoot("tags")]
@@ -214,9 +250,18 @@ namespace SPNATI_Character_Editor
 
 		public static int Compare(string tag1, string tag2)
 		{
-			int index1 = _tagOrder.IndexOf(tag1);
-			int index2 = _tagOrder.IndexOf(tag2);
-			return index1.CompareTo(index2);
+			Trigger t1 = GetTrigger(tag1);
+			Trigger t2 = GetTrigger(tag2);
+			int compare = t1.Group.CompareTo(t2.Group);
+			if (compare == 0)
+			{
+				compare = t1.GroupOrder.CompareTo(t2.GroupOrder);
+			}
+			if (compare == 0)
+			{
+				compare = tag1.CompareTo(tag2);
+			}
+			return compare;
 		}
 
 		public static void AddGroup(TextGroup group)
@@ -255,7 +300,8 @@ namespace SPNATI_Character_Editor
 		public static bool IsVariableAvailable(string tag, string variable)
 		{
 			Trigger trigger;
-			string varName = variable.ToLower();
+			string[] pieces = variable.ToLower().Split('.');
+			string varName = pieces[0];
 			if (varName.ToLower() == "clothes")
 				varName = "clothing";
 			Variable v = VariableDatabase.Get(varName);
@@ -263,7 +309,7 @@ namespace SPNATI_Character_Editor
 				return true;
 			if (_triggers.TryGetValue(tag, out trigger))
 			{
-				return trigger.AvailableVariables.Contains(variable.ToLower());
+				return trigger.AvailableVariables.Contains(varName);
 			}
 			return false;
 		}
@@ -400,4 +446,67 @@ namespace SPNATI_Character_Editor
 			return t1.RelatedGroup == t2.RelatedGroup && t2.RelatedGroup > 0;
 		}
 	}
+
+	public class TriggerProvider : IRecordProvider<Trigger>
+	{
+		public bool AllowsNew { get { return false; } }
+
+		public bool TrackRecent { get { return false; } }
+
+		public IRecord Create(string key)
+		{
+			throw new NotImplementedException();
+		}
+
+		public void Delete(IRecord record)
+		{
+			throw new NotImplementedException();
+		}
+
+		public ListViewItem FormatItem(IRecord record)
+		{
+			ListViewItem item = new ListViewItem(new string[] { record.Key, record.Name });
+			return item;
+		}
+
+		public string[] GetColumns()
+		{
+			return new string[] { "Tag", "Label" };
+		}
+
+		public string GetLookupCaption()
+		{
+			return "Select a Case Type";
+		}
+
+		public List<IRecord> GetRecords(string text)
+		{
+			text = text.ToLower();
+			List<IRecord> list = new List<IRecord>();
+			foreach (Trigger record in TriggerDatabase.Triggers)
+			{
+				if (record.Key.ToLower().Contains(text) || record.Name.ToLower().Contains(text))
+				{
+					//partial match
+					list.Add(record);
+				}
+			}
+			return list;
+		}
+
+		public void SetContext(object context)
+		{
+			
+		}
+
+		public void Sort(List<IRecord> list)
+		{
+			list.Sort((r1, r2) =>
+			{
+				Trigger t1 = r1 as Trigger;
+				Trigger t2 = r2 as Trigger;
+				return TriggerDatabase.Compare(t1.Tag, t2.Tag);
+			});
+		}
+	}
 }
diff --git a/editor source/SPNATI Character Editor/DataStructures/Variable.cs b/editor source/SPNATI Character Editor/DataStructures/Variable.cs
index 6bc501d03ee1e853b186a87d8fe6f25fec18668e..9a836283c55ed748f2908e87acf297708f48e07b 100644
--- a/editor source/SPNATI Character Editor/DataStructures/Variable.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/Variable.cs	
@@ -24,7 +24,7 @@ namespace SPNATI_Character_Editor
 		public bool UseMarkers;
 
 		[XmlElement("function")]
-		public List<VariableFunction> Functions;
+		public List<VariableFunction> Functions = new List<VariableFunction>();
 
 		public bool HasFunctions()
 		{
@@ -59,7 +59,7 @@ namespace SPNATI_Character_Editor
 			{
 				yield return func;
 			}
-			if (UseMarkers)
+			if (UseMarkers && character != null)
 			{
 				foreach (Marker marker in character.Markers.Values)
 				{
diff --git a/editor source/SPNATI Character Editor/DataStructures/VariableDatabase.cs b/editor source/SPNATI Character Editor/DataStructures/VariableDatabase.cs
index 9140623dee030f3e79a90dda607c5b776ef06cf6..d811baf40bf9db13303923c9f1ef7f85a83f3568 100644
--- a/editor source/SPNATI Character Editor/DataStructures/VariableDatabase.cs	
+++ b/editor source/SPNATI Character Editor/DataStructures/VariableDatabase.cs	
@@ -7,6 +7,7 @@ namespace SPNATI_Character_Editor
 	public static class VariableDatabase
 	{
 		private static Dictionary<string, Variable> _variables = new Dictionary<string, Variable>();
+		private static Dictionary<string, Variable> _tempVariables = new Dictionary<string, Variable>();
 		private static List<Variable> _globalVariables = new List<Variable>();
 
 		public static void Load()
@@ -55,7 +56,19 @@ namespace SPNATI_Character_Editor
 		/// <returns></returns>
 		public static Variable Get(string name)
 		{
-			return _variables.Get(name);
+			Variable v = _variables.Get(name);
+			if (v == null)
+			{
+				v = _tempVariables.Get(name);
+				if (v == null)
+				{
+					v = new Variable();
+					v.Description = "Case-specific temporary variable.";
+					v.Name = name;
+					_tempVariables[name] = v;
+				}
+			}
+			return v;
 		}
 	}
 
@@ -65,4 +78,37 @@ namespace SPNATI_Character_Editor
 		[XmlElement("variable")]
 		public List<Variable> Variables;
 	}
+
+	public static class StyleDatabase
+	{
+		private static Dictionary<string, StyleRule> _styles = new Dictionary<string, StyleRule>();
+
+		static StyleDatabase()
+		{
+			_styles.Add("!reset", new StyleRule() { ClassName = "!reset", Description = "Resets style to default" });
+			_styles.Add("b", new StyleRule() { ClassName = "b", Description = "Bold text" });
+			_styles.Add("i", new StyleRule() { ClassName = "i", Description = "Italics text" });
+			_styles.Add("u", new StyleRule() { ClassName = "u", Description = "Underline" });
+			_styles.Add("s", new StyleRule() { ClassName = "s", Description = "Strikethrough" });
+			_styles.Add("highlight", new StyleRule() { ClassName = "highlight", Description = "Highlighted text" });
+			_styles.Add("mono", new StyleRule() { ClassName = "mono", Description = "Monospace font" });
+			_styles.Add("small", new StyleRule() { ClassName = "small", Description = "Smaller text" });
+			_styles.Add("big", new StyleRule() { ClassName = "big", Description = "Bigger text" });
+		}
+
+		public static IEnumerable<StyleRule> GlobalStyles
+		{
+			get { return _styles.Values; }
+		}
+
+		public static StyleRule Get(string className, Character character)
+		{
+			StyleRule rule = _styles.Get(className);
+			if (rule == null && character.Styles != null)
+			{
+				rule = character.Styles.Rules.Find(r => r.ClassName == className);
+			}
+			return rule;			
+		}
+	}
 }
diff --git a/editor source/SPNATI Character Editor/DesktopMessages.cs b/editor source/SPNATI Character Editor/DesktopMessages.cs
index ec372c6f352fb8d3032871c807d832f544b0e4cb..d209ef88bd4b60638e6c50c165743bbf2a5a9d09 100644
--- a/editor source/SPNATI Character Editor/DesktopMessages.cs	
+++ b/editor source/SPNATI Character Editor/DesktopMessages.cs	
@@ -16,5 +16,10 @@
 		/// Macros were changed from the macro manager
 		/// </summary>
 		public const int MacrosUpdated = 3;
+
+		/// <summary>
+		/// Sent when setting for showing images is toggled
+		/// </summary>
+		public const int ToggleImages = 4;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/DialogueLine.cs b/editor source/SPNATI Character Editor/DialogueLine.cs
index ce36819b44576e8fc7b683da46cb16ed8ced0720..09893a0f65bef83ee71fd93f85afecf033e73027 100644
--- a/editor source/SPNATI Character Editor/DialogueLine.cs	
+++ b/editor source/SPNATI Character Editor/DialogueLine.cs	
@@ -7,16 +7,26 @@ using System.ComponentModel;
 namespace SPNATI_Character_Editor
 {
 	/// <summary>
-	/// A single ine of dialogue and its pose
+	/// A single line of dialogue and its pose
 	/// </summary>
 	public class DialogueLine
 	{
 		[XmlAttribute("img")]
 		public string Image;
 
+		[XmlIgnore]
+		public Dictionary<int, LineImage> StageImages = new Dictionary<int, LineImage>();
+
 		[XmlText]
 		public string Text;
 
+		/// <summary>
+		/// Line will only play once
+		/// </summary>
+		[DefaultValue(0)]
+		[XmlAttribute("oneShotId")]
+		public int OneShotId;
+
 		[XmlAttribute("marker")]
 		public string Marker;
 
@@ -67,6 +77,7 @@ namespace SPNATI_Character_Editor
 		public bool IsMarkerPersistent;
 
 		public static readonly string[] ArrowDirections = new string[] { "", "down", "left", "right", "up" };
+		public static readonly string[] AILevels = new string[] { "", "throw", "bad", "average", "good", "best" };
 
 		public DialogueLine()
 		{
@@ -87,13 +98,22 @@ namespace SPNATI_Character_Editor
 		public DialogueLine Copy()
 		{
 			DialogueLine copy = MemberwiseClone() as DialogueLine;
+			copy.StageImages = new Dictionary<int, LineImage>();
+			foreach (KeyValuePair<int, LineImage> kvp in StageImages)
+			{
+				copy.StageImages[kvp.Key] = new LineImage(kvp.Value.Image, kvp.Value.IsGenericImage);
+			}
 			return copy;
 		}
 
-		public override int GetHashCode()
+		/// <summary>
+		/// Gets a hash code not including the image
+		/// </summary>
+		/// <param name="line"></param>
+		/// <returns></returns>
+		public int GetHashCodeWithoutImage()
 		{
-			int hash = (Image ?? string.Empty).GetHashCode();
-			hash = (hash * 397) ^ (Text ?? string.Empty).GetHashCode();
+			int hash = (Text ?? string.Empty).GetHashCode();
 			hash = (hash * 397) ^ (Marker ?? string.Empty).GetHashCode();
 			hash = (hash * 397) ^ (Direction ?? string.Empty).GetHashCode();
 			hash = (hash * 397) ^ (Location ?? string.Empty).GetHashCode();
@@ -105,6 +125,14 @@ namespace SPNATI_Character_Editor
 			hash = (hash * 397) ^ (CollectibleId ?? string.Empty).GetHashCode();
 			hash = (hash * 397) ^ (CollectibleValue ?? string.Empty).GetHashCode();
 			hash = (hash * 397) ^ IsMarkerPersistent.GetHashCode();
+			hash = (hash * 397) ^ (OneShotId > 0 ? OneShotId : -1);
+			return hash;
+		}
+
+		public override int GetHashCode()
+		{
+			int hash = GetHashCodeWithoutImage();
+			hash = (hash * 397) ^ (Image ?? string.Empty).GetHashCode();
 			return hash;
 		}
 
@@ -198,9 +226,10 @@ namespace SPNATI_Character_Editor
 		/// <param name="tag"></param>
 		/// <param name="text"></param>
 		/// <returns></returns>
-		public static List<string> GetInvalidVariables(string tag, string text)
+		public static List<string> GetInvalidVariables(Case dialogueCase, string text)
 		{
-			Regex varRegex = new Regex(@"~\w*~", RegexOptions.IgnoreCase);
+			string tag = dialogueCase.Tag;
+			Regex varRegex = new Regex(@"~[^\s~]*~", RegexOptions.IgnoreCase);
 			List<string> invalidVars = new List<string>();
 			if (text == "~silent~")
 			{
@@ -212,12 +241,29 @@ namespace SPNATI_Character_Editor
 				string variable = match.ToString().Trim('~');
 				if (!TriggerDatabase.IsVariableAvailable(tag, variable))
 				{
-					invalidVars.Add(variable);
+					//check filters for variables
+					TargetCondition filter = dialogueCase.Conditions.Find(c => c.Variable == variable);
+					if (filter == null)
+					{
+						invalidVars.Add(variable);
+					}
 				}
 			}
 			return invalidVars;
 		}
 
+		/// <summary>
+		/// Sets a dialogue line to use the generic version of a particular image
+		/// </summary>
+		public void GeneralizeImage(DialogueLine line)
+		{
+			string extension = line.ImageExtension ?? Path.GetExtension(line.Image);
+			ImageExtension = extension;
+			line.ImageExtension = extension;
+			Image = GetDefaultImage(line.Image);
+			IsGenericImage = line.IsGenericImage;
+		}
+
 		public bool HasAdvancedMarker
 		{
 			get
@@ -226,4 +272,21 @@ namespace SPNATI_Character_Editor
 			}
 		}
 	}
+
+	public class LineImage
+	{
+		public string Image;
+		public bool IsGenericImage;
+
+		public LineImage(string img, bool generic)
+		{
+			Image = img;
+			IsGenericImage = generic;
+		}
+
+		public override string ToString()
+		{
+			return Image;
+		}
+	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/AnimationHelpers.cs b/editor source/SPNATI Character Editor/EpilogueEditing/AnimationHelpers.cs
index d69c124fdfe41c114b8378ab31396fee4919e351..e99919fadb064634e757a93729b642c4401ad06b 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/AnimationHelpers.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/AnimationHelpers.cs	
@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Drawing;
 
 namespace SPNATI_Character_Editor
 {
@@ -20,6 +21,18 @@ namespace SPNATI_Character_Editor
 			{
 				return t >= 1 ? nextValue : lastValue;
 			}
+			else if (type == typeof(Color))
+			{
+				Color lastLast = (Color)lastLastValue;
+				Color last = (Color)lastValue;
+				Color next = (Color)nextValue;
+				Color nextNext = (Color)nextNextValue;
+				int r = (int)Interpolate(last.R, next.R, interpolationMode, t, lastLast.R, nextNext.R);
+				int g = (int)Interpolate(last.G, next.G, interpolationMode, t, lastLast.G, nextNext.G);
+				int b = (int)Interpolate(last.B, next.B, interpolationMode, t, lastLast.B, nextNext.B);
+				int a = (int)Interpolate(last.A, next.A, interpolationMode, t, lastLast.A, nextNext.A);
+				return Color.FromArgb(a, r, g, b);
+			}
 			else
 			{
 				throw new Exception($"Cannot interpolate type: {type.Name}");
@@ -118,7 +131,11 @@ namespace SPNATI_Character_Editor
 
 			for (int i = 0; i < sorted.Count; i++)
 			{
-				list.Move(list.IndexOf(sorted[i]), i);
+				int oldIndex = list.IndexOf(sorted[i]);
+				if (oldIndex != i)
+				{
+					list.Move(oldIndex, i);
+				}
 			}
 		}
 	}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/AnimatedProperty.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/AnimatedProperty.cs
deleted file mode 100644
index 562e75c0869bcfd9d5a960e067a1e4024a5029ef..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/AnimatedProperty.cs	
+++ /dev/null
@@ -1,233 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Desktop;
-using Desktop.CommonControls.PropertyControls;
-using Desktop.DataStructures;
-using System.Collections.ObjectModel;
-
-namespace SPNATI_Character_Editor.EpilogueEditor
-{
-	public class AnimatedProperty : BindableObject, ILabel
-	{
-		public event EventHandler LabelChanged;
-
-		public string PropertyName
-		{
-			get { return Get<string>(); }
-			set
-			{
-				Set(value);
-				LabelChanged?.Invoke(this, EventArgs.Empty);
-			}
-		}
-
-		[Boolean(DisplayName = "Looping", GroupOrder = 0, Description = "When checked, this property's keyframes will loop indefinitely.")]
-		public bool Looped
-		{
-			get { return Get<bool>(); }
-			set { Set(value); }
-		}
-
-		public AnimatedValue<string> Ease
-		{
-			get { return Get<AnimatedValue<string>>(); }
-			set { Set(value); }
-		}
-
-		public AnimatedValue<string> Interpolation
-		{
-			get { return Get<AnimatedValue<string>>(); }
-			set { Set(value); }
-		}
-
-		public AnimatedProperty()
-		{
-		}
-
-		public AnimatedProperty(string name)
-		{
-			PropertyName = name;
-			Ease = new AnimatedValue<string>();
-			Interpolation = new AnimatedValue<string>();
-			Interpolation.SetValue(0, name == "Src" ? "none" : "linear");
-		}
-
-		public override string ToString()
-		{
-			return PropertyName;
-		}
-
-		public string GetLabel()
-		{
-			return $"Animation Settings: {PropertyName}";
-		}
-
-		public string ToKey(float time)
-		{
-			return $"{(Looped ? "1" : "")}|{(Ease.GetValue(time) ?? "")}|{(Interpolation.GetValue(time) ?? "")}";
-		}
-	}
-
-	public class AnimatedValue<T> : BindableObject
-	{
-		public ObservableCollection<TimedValue<T>> Values
-		{
-			get { return Get<ObservableCollection<TimedValue<T>>>(); }
-			set { Set(value); }
-		}
-
-		public AnimatedValue()
-		{
-			Values = new ObservableCollection<TimedValue<T>>();
-		}
-
-		public T GetValue(float time)
-		{
-			for (int i = Values.Count - 1; i >= 0; i--)
-			{
-				TimedValue<T> timedValue = Values[i];
-				if (i == 0 || timedValue.Time <= time)
-				{
-					return timedValue.Value;
-				}
-			}
-			return default(T);
-		}
-
-		public void SetValue(float time, T value)
-		{
-			for (int i = 0; i < Values.Count; i++)
-			{
-				TimedValue<T> current = Values[i];
-				if (current.Time == time)
-				{
-					current.Value = value;
-					return;
-				}
-				else if (current.Time > time)
-				{
-					Values.Insert(i, new TimedValue<T>(time, value));
-					return;
-				}
-			}
-			Values.Add(new TimedValue<T>(time, value));
-		}
-
-		public void RemoveValue(float time)
-		{
-			for (int i = 0; i < Values.Count; i++)
-			{
-				if (Values[i].Time == time)
-				{
-					Values.RemoveAt(i);
-					break;
-				}
-			}
-		}
-
-		public override string ToString()
-		{
-			if (Values.Count == 0)
-			{
-				return null;
-			}
-			return Values[0].Value?.ToString();
-		}
-	}
-
-	public class TimedValue<T> : BindableObject
-	{
-		public float Time
-		{
-			get { return Get<float>(); }
-			set { Set(value); }
-		}
-
-		public T Value
-		{
-			get { return Get<T>(); }
-			set { Set(value); }
-		}
-
-		public TimedValue()
-		{
-		}
-
-		public TimedValue(float time, T value)
-		{
-			Time = time;
-			Value = value;
-		}
-
-		public override string ToString()
-		{
-			return $"@{Time}s: {Value}";
-		}
-	}
-
-	/// <summary>
-	/// Data container for values of a property across keyframes in a sprite
-	/// </summary>
-	public class AnimatedPropertyClipboardData : ICommand
-	{
-		private List<string> _properties = new List<string>();
-		private List<Tuple<float, string, object>> _data = new List<Tuple<float, string, object>>();
-		private LiveSprite _fromSprite;
-		private LiveSprite _sprite;
-
-		public AnimatedPropertyClipboardData(LiveSprite sprite, List<string> properties)
-		{
-			_properties.AddRange(properties);
-			_fromSprite = sprite;
-			foreach (LiveKeyframe kf in sprite.Keyframes)
-			{
-				foreach (string property in properties)
-				{
-					if (kf.HasProperty(property))
-					{
-						object value = kf.Get<object>(property);
-						_data.Add(new Tuple<float, string, object>(kf.Time, property, value));
-					}
-				}
-			}
-		}
-
-		/// <summary>
-		/// Applies this data to a sprite
-		/// </summary>
-		/// <param name="sprite"></param>
-		public void Apply(LiveSprite sprite)
-		{
-			_sprite = sprite;
-			Do();
-		}
-
-		public void Do()
-		{
-			foreach (Tuple<float, string, object> tuple in _data)
-			{
-				float time = tuple.Item1;
-				string property = tuple.Item2;
-				object value = tuple.Item3;
-				LiveKeyframe frame = _sprite.Keyframes.Find(kf => kf.Time == time);
-				if (frame == null)
-				{
-					frame = _sprite.AddKeyframe(time);
-				}
-				frame.Set(value, property);
-			}
-
-			foreach (string property in _properties)
-			{
-				AnimatedProperty fromProp = _fromSprite.GetAnimationProperties(property);
-				AnimatedProperty toProp = _sprite.GetAnimationProperties(property);
-				fromProp.CopyPropertiesInto(toProp);
-			}
-		}
-
-		public void Undo()
-		{
-			throw new NotImplementedException();
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveAnimatedObject.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveAnimatedObject.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e9cc4dc2aac625c7df9c27de985839aa021728fa
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveAnimatedObject.cs	
@@ -0,0 +1,1127 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Drawing;
+using System.Globalization;
+using System.Linq;
+using Desktop;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	/// <summary>
+	/// Root class for LiveObjects that can be animated with key frames
+	/// </summary>
+	public abstract class LiveAnimatedObject : LiveObject
+	{
+		public Character Character;
+		public bool DisplayPastEnd = true;
+
+		private float _lastPlaybackTime;
+		private float _lastElapsedTime;
+
+		public float Length
+		{
+			get
+			{
+				if (Keyframes.Count > 1)
+				{
+					float time = Keyframes[Keyframes.Count - 1].Time;
+					return time;
+				}
+				return Get<float>();
+			}
+			set { Set(value); }
+		}
+
+		public bool IsPreview;
+
+		public ObservableCollection<string> Properties
+		{
+			get { return Get<ObservableCollection<string>>(); }
+			set { Set(value); }
+		}
+
+		public ObservableSet<string> AnimatedProperties
+		{
+			get { return Get<ObservableSet<string>>(); }
+			set { Set(value); }
+		}
+
+		public ObservableCollection<LiveKeyframe> Keyframes
+		{
+			get { return Get<ObservableCollection<LiveKeyframe>>(); }
+			set { Set(value); }
+		}
+
+		public override bool IsVisible
+		{
+			get { return Time >= Start && (IsPreview || DisplayPastEnd || Time <= Start + Length || HasLoops()); }
+		}
+
+		public event EventHandler<LiveKeyframe> KeyframeChanged;
+
+		public LiveAnimatedObject()
+		{
+			Properties = new ObservableCollection<string>();
+			Keyframes = new ObservableCollection<LiveKeyframe>();
+			AnimatedProperties = new ObservableSet<string>();
+		}
+
+		protected override void OnCopyTo(LiveObject copy)
+		{
+			LiveAnimatedObject animatedCopy = copy as LiveAnimatedObject;
+			if (animatedCopy != null)
+			{
+				foreach (LiveKeyframe kf in animatedCopy.Keyframes)
+				{
+					kf.Data = animatedCopy;
+					kf.PropertyChanged += Kf_PropertyChanged;
+				}
+			}
+		}
+
+		public abstract Type GetKeyframeType();
+
+		public LiveKeyframe CreateKeyframe(float time)
+		{
+			LiveKeyframe kf = Activator.CreateInstance(GetKeyframeType()) as LiveKeyframe;
+			kf.Time = time;
+			return kf;
+		}
+
+		/// <summary>
+		/// Adds a property value to a keyframe at the given time
+		/// </summary>
+		/// <param name="time">Time in seconds from start </param>
+		/// <param name="propName"></param>
+		/// <param name="serializedValue"></param>
+		/// <returns>Keyframe at that point</returns>
+		protected override void AddValue<T>(float time, string propName, string serializedValue, bool addAnimBreak)
+		{
+			if (string.IsNullOrEmpty(serializedValue))
+			{
+				return;
+			}
+
+			if (!AnimatedProperties.Contains(propName))
+			{
+				AddAnimatedProperty(propName);
+			}
+			LiveKeyframe keyframe = Keyframes.Find(k => k.Time == time);
+			if (keyframe == null)
+			{
+				keyframe = AddKeyframe(time);
+			}
+
+			if (addAnimBreak)
+			{
+				bool isSplit = keyframe.HasProperty(propName);
+				keyframe.GetMetadata(propName, true).FrameType = isSplit ? KeyframeType.Split : KeyframeType.Begin;
+			}
+
+			object val = null;
+			Type propType = typeof(T);
+			if (propType == typeof(string))
+			{
+				val = serializedValue;
+			}
+			else if (propType == typeof(float))
+			{
+				float valFloat;
+				float.TryParse(serializedValue, NumberStyles.Number, CultureInfo.InvariantCulture, out valFloat);
+				val = valFloat;
+			}
+			else if (propType == typeof(int))
+			{
+				int valInt;
+				int.TryParse(serializedValue, out valInt);
+				val = valInt;
+			}
+			else if (propType == typeof(Color))
+			{
+				try
+				{
+					val = ColorTranslator.FromHtml(serializedValue);
+				}
+				catch { }
+			}
+			else
+			{
+				throw new ArgumentException($"Type {typeof(T).Name} not supported.");
+			}
+			keyframe.Set(val, propName);
+		}
+
+		public LiveKeyframe AddKeyframe(float time)
+		{
+			LiveKeyframe kf = CreateKeyframe(time);
+			AddKeyframe(kf);
+			return kf;
+		}
+
+		public void AddKeyframe(LiveKeyframe kf)
+		{
+			kf.Data = this;
+			kf.PropertyChanged += Kf_PropertyChanged;
+			float time = kf.Time;
+			bool added = false;
+			for (int i = 0; i < Keyframes.Count; i++)
+			{
+				LiveKeyframe other = Keyframes[i];
+				if (other.Time > time)
+				{
+					Keyframes.Insert(i, kf);
+					added = true;
+					break;
+				}
+			}
+			if (!added)
+			{
+				Keyframes.Add(kf);
+			}
+
+			foreach (string prop in kf.TrackedProperties)
+			{
+				if (kf.HasProperty(prop))
+				{
+					UpdateProperty(prop);
+				}
+			}
+		}
+
+		public void RemoveKeyframe(LiveKeyframe kf)
+		{
+			kf.PropertyChanged -= Kf_PropertyChanged;
+			kf.Data = null;
+			Keyframes.Remove(kf);
+
+			foreach (string prop in kf.TrackedProperties)
+			{
+				if (kf.HasProperty(prop))
+				{
+					UpdateProperty(prop);
+				}
+			}
+		}
+
+		private void ResortKeyframe(LiveKeyframe kf)
+		{
+			int index = Keyframes.IndexOf(kf);
+			if (index == -1) { return; }
+			float time = kf.Time;
+			bool added = false;
+			for (int i = 0; i < Keyframes.Count; i++)
+			{
+				LiveKeyframe other = Keyframes[i];
+				if (i == index)
+				{
+					continue;
+				}
+				if (other.Time > time)
+				{
+					added = true;
+					int newIndex = i > index ? i - 1 : i;
+					if (newIndex != index)
+					{
+						Keyframes.RemoveAt(index);
+						Keyframes.Insert(newIndex, kf);
+					}
+					break;
+				}
+			}
+			if (!added && index != Keyframes.Count - 1)
+			{
+				Keyframes.Add(kf);
+			}
+		}
+
+		private void Kf_PropertyChanged(object sender, PropertyChangedEventArgs e)
+		{
+			LiveKeyframe frame = sender as LiveKeyframe;
+			if (e.PropertyName == "Time")
+			{
+				//resort
+				ResortKeyframe(sender as LiveKeyframe);
+			}
+			else if (e.PropertyName == "PropertyMetadata")
+			{
+				//just don't do the else block
+			}
+			else if (frame.TrackedProperties.Contains(e.PropertyName))
+			{
+				UpdateProperty(e.PropertyName);
+
+				//wipe out the frame if it has no properties remaining
+				if (frame.IsEmpty && frame.Time > 0)
+				{
+					RemoveKeyframe(frame);
+				}
+
+				KeyframeChanged?.Invoke(this, frame);
+			}
+		}
+
+		/// <summary>
+		/// Updates the Properties array when a property changes
+		/// </summary>
+		/// <param name="property"></param>
+		private void UpdateProperty(string property)
+		{
+			bool hasProperty = AnimatedProperties.Contains(property);
+			int count = Keyframes.Count(kf => kf.HasProperty(property));
+			if (count == 0 && hasProperty)
+			{
+				//need to remove the property
+				RemoveAnimatedProperty(property);
+			}
+			else if (count > 0 && !hasProperty)
+			{
+				//need to add the property
+				AddAnimatedProperty(property);
+			}
+		}
+
+		private void AddAnimatedProperty(string property)
+		{
+			PropertyDefinition propertyDef = Definitions.Instance.Get<PropertyDefinition>(property);
+			bool inserted = false;
+			if (propertyDef != null)
+			{
+				for (int i = 0; i < Properties.Count; i++)
+				{
+					string prop = Properties[i];
+					PropertyDefinition otherDef = Definitions.Instance.Get<PropertyDefinition>(prop);
+					if (otherDef == null) { continue; }
+					int compare = propertyDef.CompareTo(otherDef);
+					if (compare < 0)
+					{
+						Properties.Insert(i, property);
+						inserted = true;
+						break;
+					}
+				}
+			}
+			if (!inserted)
+			{
+				Properties.Add(property);
+			}
+			AnimatedProperties.Add(property);
+		}
+
+		private void RemoveAnimatedProperty(string property)
+		{
+			Properties.Remove(property);
+			AnimatedProperties.Remove(property);
+		}
+
+		/// <summary>
+		/// Gets the time a particular property is animating.
+		/// </summary>
+		/// <remarks>
+		/// If the property's 1st non-0 keyframe has the same value as time 0, then that frame will be treated as the starting frame
+		/// </remarks>
+		/// <param name="property"></param>
+		/// <returns></returns>
+		private float GetPropertyDuration(string property, float time, out float start, out float end)
+		{
+			start = 0;
+			end = 0;
+			List<LiveKeyframe> validFrames = new List<LiveKeyframe>();
+			for (int i = 0; i < Keyframes.Count; i++)
+			{
+				LiveKeyframe kf = Keyframes[i];
+				if (!kf.HasProperty(property)) { continue; }
+
+				KeyframeType type = kf.GetFrameType(property);
+				if (kf.Time <= time && type != KeyframeType.Normal)
+				{
+					validFrames.Clear();
+				}
+				else if (kf.Time > time && type == KeyframeType.Begin)
+				{
+					break;
+				}
+
+				validFrames.Add(kf);
+
+				if (kf.Time > time && type == KeyframeType.Split)
+				{
+					break;
+				}
+			}
+
+			if (validFrames.Count == 0)
+			{
+				return 0;
+			}
+
+			start = validFrames[0].Time;
+			end = validFrames[validFrames.Count - 1].Time;
+			return end - start;
+		}
+
+		/// <summary>
+		/// Gets the keyframe that starts a block
+		/// </summary>
+		/// <param name="time"></param>
+		/// <returns></returns>
+		public virtual LiveKeyframe GetBlockKeyframe(string property, float time)
+		{
+			LiveKeyframe firstFrame = null;
+			for (int i = Keyframes.Count - 1; i >= 0; i--)
+			{
+				LiveKeyframe kf = Keyframes[i];
+				if (!kf.HasProperty(property)) { continue; }
+
+				firstFrame = kf;
+				if (i == 0 || kf.Time <= time)
+				{
+					LiveKeyframeMetadata metadata = kf.GetMetadata(property, false);
+					if (metadata != null && (metadata.FrameType == KeyframeType.Begin || metadata.FrameType == KeyframeType.Split))
+					{
+						return kf;
+					}
+				}
+			}
+
+			if (Keyframes.Count == 0)
+			{
+				return null;
+			}
+			return firstFrame ?? Keyframes[0];
+		}
+
+		/// <summary>
+		/// Gets the metadata for a keyframe block that encompasses the given time
+		/// </summary>
+		/// <param name="time"></param>
+		/// <returns></returns>
+		public LiveKeyframeMetadata GetBlockMetadata(string property, float time)
+		{
+			LiveKeyframe kf = GetBlockKeyframe(property, time);
+			if (kf == null)
+			{
+				return new LiveKeyframeMetadata(property);
+			}
+			return kf.GetMetadata(property, false);
+		}
+
+		/// <summary>
+		/// Gets the value of a property at the given point in time
+		/// </summary>
+		/// <typeparam name="T">Property type</typeparam>
+		/// <param name="property">Property name</param>
+		/// <param name="time">Time in seconds from the start of the anim</param>
+		/// <param name="defaultValue">Value to use if no frames define this property</param>
+		/// <returns>Interpolated value at the given point in time</returns>
+		public T GetPropertyValue<T>(string property, float time, float offset, T defaultValue)
+		{
+			return GetPropertyValue<T>(property, time, offset, defaultValue, null, null, null);
+		}
+		/// <summary>
+		/// Gets the value of a property at the given point in time
+		/// </summary>
+		/// <typeparam name="T">Property type</typeparam>
+		/// <param name="property">Property name</param>
+		/// <param name="time">Time in seconds from the start of the anim</param>
+		/// <param name="defaultValue">Value to use if no frames define this property</param>
+		/// <param name="easeOverride">Ease to use instead of the property's defined ease</param>
+		/// <param name="interpolationOverride">Interpolation to use instead of the property's defined interpolation</param>
+		/// <returns>Interpolated value at the given point in time</returns>
+		public T GetPropertyValue<T>(string property, float time, float offset, T defaultValue, string easeOverride, string interpolationOverride, bool? loopOverride)
+		{
+			float start;
+			float end;
+			float t = GetInterpolatedTime(property, time, offset, easeOverride, loopOverride, out start, out end);
+			t = start + t * (end - start);
+
+			Type parentType = GetKeyframeType();
+
+			LiveKeyframeMetadata metadata = GetBlockMetadata(property, t);
+			string frameInterp = metadata.Interpolation;
+			string interpolation = interpolationOverride ?? frameInterp;
+			if (string.IsNullOrEmpty(frameInterp) || frameInterp == "none")
+			{
+				interpolation = "none";
+			}
+
+			LiveKeyframe previousFrame = null;
+			LiveKeyframe previousPreviousFrame = null;
+			LiveKeyframe nextFrame = null;
+			LiveKeyframe nextNextFrame = null;
+			bool foundNext = false;
+			bool foundNextNext = false;
+			Stack<LiveKeyframe> validFrames = new Stack<LiveKeyframe>();
+
+			for (int i = 0; i < Keyframes.Count; i++)
+			{
+				LiveKeyframe kf = Keyframes[i];
+				if (!kf.HasProperty(property)) { continue; }
+
+				KeyframeType type = kf.GetFrameType(property);
+				if (kf.Time <= t && type != KeyframeType.Normal)
+				{
+					foundNext = false;
+					foundNextNext = false;
+					validFrames.Clear();
+				}
+				else if (kf.Time > t && type == KeyframeType.Begin)
+				{
+					break;
+				}
+				validFrames.Push(kf);
+				if (kf.Time > t)
+				{
+					if (foundNext)
+					{
+						foundNextNext = true;
+						break;
+					}
+					foundNext = true;
+				}
+			}
+
+			if (foundNextNext && validFrames.Count > 0)
+			{
+				nextNextFrame = validFrames.Pop();
+			}
+			if (validFrames.Count > 0)
+			{
+				nextFrame = validFrames.Pop();
+			}
+			if (validFrames.Count > 0)
+			{
+				previousFrame = validFrames.Pop();
+			}
+			if (validFrames.Count > 0)
+			{
+				previousPreviousFrame = validFrames.Pop();
+			}
+
+			if (nextFrame != null)
+			{
+				previousFrame = previousFrame ?? nextFrame;
+				nextNextFrame = nextNextFrame ?? nextFrame;
+				previousPreviousFrame = previousPreviousFrame ?? previousFrame;
+				object previous = previousFrame.Get<object>(property);
+				object next = nextFrame.Get<object>(property);
+				object previousPrevious = previousPreviousFrame.Get<object>(property);
+				object nextNext = nextNextFrame.Get<object>(property);
+				float prevTime = previousFrame.Time;
+				float nextTime = nextFrame.Time;
+				float frameT = nextTime == prevTime ? 0 : (t - prevTime) / (nextTime - prevTime);
+				Type propertyType = PropertyTypeInfo.GetType(parentType, property);
+				return (T)AnimationHelpers.Interpolate(propertyType, previous, next, interpolation, frameT, previousPrevious, nextNext);
+			}
+			return defaultValue;
+		}
+
+		/// <summary>
+		/// Gets a time from 0-1 where 0=first frame and 1=last frame based on a property's keyframes and animation settings
+		/// </summary>
+		/// <param name="property"></param>
+		/// <param name="time"></param>
+		/// <param name="easeOverride"></param>
+		/// <param name="interpolationOverride"></param>
+		/// <param name="start"></param>
+		/// <returns></returns>
+		public float GetInterpolatedTime(string property, float time, float offset, string easeOverride, bool? loopOverride, out float start, out float end)
+		{
+			time -= Start; //use relative time
+			time = Math.Max(0, time);
+
+			//figure out this property's duration, which is from the first frame past time 0 if that frame has the same value as time 0, otherwise from time 0, to the last frame modifying this property
+			start = 0;
+			end = 0;
+			float duration = GetPropertyDuration(property, time, out start, out end);
+
+			LiveKeyframeMetadata metadata = GetBlockMetadata(property, time);
+			string ease = easeOverride ?? metadata.Ease;
+			bool looped = loopOverride.HasValue ? loopOverride.Value : metadata.Looped;
+
+			if (looped)
+			{
+				time += offset;
+			}
+
+			if (time < start)
+			{
+				return 0;
+			}
+			else if (time > end)
+			{
+				if (looped)
+				{
+					if (duration > 0.0001f && time > end)
+					{
+						time = Clamp(time, start, duration, metadata.Iterations, metadata.ClampMethod);
+					}
+				}
+				else
+				{
+					return 1;
+				}
+			}
+
+			float relativeTime = 0;
+			if (duration > 0)
+			{
+				relativeTime = (time - start) / duration;
+			}
+
+			float t = AnimationHelpers.Ease(ease, relativeTime);
+			return t;
+		}
+
+		private float Clamp(float t, float start, float duration, int iterations, string clampMethod)
+		{
+			if (iterations > 0)
+			{
+				if (t >= iterations * duration)
+				{
+					return start + duration;
+				}
+			}
+
+			switch (clampMethod)
+			{
+				case "clamp":
+					return t > start + duration ? start + duration : t;
+				case "mirror":
+					t -= start;
+					float d2 = duration * 2;
+					t %= d2;
+					t = t > duration ? d2 - t : t;
+					t += start;
+					return t;
+				default:
+					while (t > start + duration)
+					{
+						t -= duration;
+					}
+					return t;
+			}
+		}
+
+		/// <summary>
+		/// Moves one or more properties from one keyframe to another (generating a new frame if it needs to)
+		/// </summary>
+		/// <param name="sourceFrame">Keyframe that the property originated on</param>
+		/// <param name="time">Relative time to move the property to</param>
+		/// <param name="targetFrame">Frame to move to. If not provided, a new frame at time will be generated</param>
+		/// <returns>Keyframe containing the property after moving it</returns>
+		public LiveKeyframe MoveProperty(LiveKeyframe sourceFrame, List<string> properties, float time, LiveKeyframe targetFrame)
+		{
+			if (targetFrame != null && !Keyframes.Contains(targetFrame))
+			{
+				AddKeyframe(targetFrame);
+			}
+			targetFrame = targetFrame ?? Keyframes.Find(k => k.Time == time);
+			foreach (string property in properties)
+			{
+				if (!sourceFrame.HasProperty(property))
+				{
+					throw new ArgumentException($"Cannot move a property that doesn't exist: {property}.", nameof(properties));
+				}
+			}
+			if (targetFrame == sourceFrame)
+			{
+				//if moving onto the same keyframe, just update the time
+				targetFrame.Time = time;
+			}
+			else
+			{
+				//if the affected properties are the only properties on the sourceFrame, and there is no targetFrame, then just move the whole frame
+				if (targetFrame == null && sourceFrame.PropertyCount == properties.Count)
+				{
+					sourceFrame.Time = time;
+					targetFrame = sourceFrame;
+				}
+				else
+				{
+					foreach (string property in properties)
+					{
+						object val = sourceFrame.Get<object>(property);
+
+						//1. Remove it from the previous keyframe, which might delete the sourceKeyframe too
+						sourceFrame.Delete(property);
+
+						//2. Create a new keyframe if needed
+						if (targetFrame == null)
+						{
+							targetFrame = AddKeyframe(time);
+						}
+
+						//3. Put the property into the target frame
+						targetFrame.Set(val, property);
+					}
+				}
+			}
+
+			return targetFrame;
+		}
+
+		/// <summary>
+		/// Copies one or more properties from a keyframe into a new, loose keyframe
+		/// </summary>
+		/// <param name="keyframe">Keyframe to copy</param>
+		/// <param name="properties">Properties to copy</param>
+		/// <returns></returns>
+		public LiveKeyframe CopyKeyframe(LiveKeyframe keyframe, HashSet<string> properties)
+		{
+			LiveKeyframe copy = CreateKeyframe(keyframe.Time);
+			if (properties.Count == 0)
+			{
+				keyframe.CopyPropertiesInto(copy);
+				return copy;
+			}
+			else
+			{
+				foreach (string property in properties)
+				{
+					copy.Set(keyframe.Get<object>(property), property);
+				}
+				return copy;
+			}
+		}
+
+		/// <summary>
+		/// Copies the properties from a keyframe into this sprite, replacing any previous properties at that time
+		/// </summary>
+		/// <param name="source">Keyframe to copy from</param>
+		/// <param name="time">Time to paste properties at</param>
+		/// <param name="target">Target frame to paste to. If not provided, a new frame will be created.</param>
+		/// <returns></returns>
+		public LiveKeyframe PasteKeyframe(LiveKeyframe source, float time, LiveKeyframe target)
+		{
+			target = target ?? Keyframes.Find(kf => kf.Time == time);
+			if (target == null)
+			{
+				target = AddKeyframe(time);
+			}
+			else if (!Keyframes.Contains(target))
+			{
+				AddKeyframe(target);
+			}
+			source.CopyPropertiesInto(target);
+			target.Time = time;
+			return target;
+		}
+
+		/// <summary>
+		/// Creates a keyframe representing the interpolated values at a particular time, without adding the frame to the sprite
+		/// </summary>
+		/// <param name="time">Relative time</param>
+		/// <returns></returns>
+		public LiveKeyframe GetInterpolatedFrame(float time)
+		{
+			LiveKeyframe frame = CreateKeyframe(time);
+
+			foreach (string property in Properties)
+			{
+				frame.Set(GetPropertyValue<object>(property, time, 0, null), property);
+			}
+
+			return frame;
+		}
+
+		public override LiveObject CreateLivePreview(float time)
+		{
+			LiveAnimatedObject copy = Copy() as LiveAnimatedObject;
+			copy.IsPreview = true;
+			copy.CenterX = CenterX;
+			copy.CenterY = CenterY;
+			copy.DisplayPastEnd = DisplayPastEnd;
+			LinkedPreview = copy;
+			copy.Data = Data;
+			copy.Hidden = false;
+			if (Keyframes.Count > 0)
+			{
+				copy.Keyframes.Clear();
+				foreach (LiveKeyframe kf in Keyframes) //use the same keyframe references so we can modify them from either the preview or the source
+				{
+					copy.Keyframes.Add(kf);
+				}
+			}
+			copy.PropertyChanged += Preview_PropertyChanged;
+			copy.Keyframes.CollectionChanged += Preview_KeyframesChanged;
+			copy.Update(time, 0, false);
+			AttachSourceListener();
+			return copy;
+		}
+
+		public override void DestroyLivePreview()
+		{
+			if (LinkedPreview == null) { return; }
+			(LinkedPreview as LiveAnimatedObject).Keyframes.CollectionChanged -= Preview_KeyframesChanged;
+			LinkedPreview.PropertyChanged -= Preview_PropertyChanged;
+			DetachSourceListener();
+			LinkedPreview = null;
+		}
+
+		/// <summary>
+		/// Raised on a preview when a property on the preview object has changed
+		/// </summary>
+		/// <param name="sender"></param>
+		/// <param name="e"></param>
+		private void Preview_PropertyChanged(object sender, PropertyChangedEventArgs e)
+		{
+			DetachSourceListener();
+			OnPreviewPropertyChanged(sender, e);
+			if (e.PropertyName == "PivotX")
+			{
+				PivotX = LinkedPreview.PivotX;
+			}
+			else if (e.PropertyName == "PivotY")
+			{
+				PivotY = LinkedPreview.PivotY;
+			}
+			AttachSourceListener();
+		}
+		protected virtual void OnPreviewPropertyChanged(object sender, PropertyChangedEventArgs e)
+		{
+
+		}
+
+		/// <summary>
+		/// Raised on a preview when the keyframes of the preview have changed to handle auto-keyframe insertion
+		/// </summary>
+		/// <param name="sender"></param>
+		/// <param name="e"></param>
+		private void Preview_KeyframesChanged(object sender, NotifyCollectionChangedEventArgs e)
+		{
+			if (e.Action == NotifyCollectionChangedAction.Add)
+			{
+				DetachSourceListener();
+				foreach (LiveKeyframe kf in e.NewItems)
+				{
+					AddKeyframe(kf);
+				}
+				AttachSourceListener();
+			}
+		}
+
+		/// <summary>
+		/// Attaches property changed listeners to a source so it can keep its preview current
+		/// </summary>
+		private void AttachSourceListener()
+		{
+			Keyframes.CollectionChanged += Source_KeyframesChanged;
+			PropertyChanged += Source_PropertyChanged;
+		}
+
+		private void DetachSourceListener()
+		{
+			Keyframes.CollectionChanged -= Source_KeyframesChanged;
+			PropertyChanged -= Source_PropertyChanged;
+		}
+
+		private void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
+		{
+			if (LinkedPreview == null) { return; }
+			if (e.PropertyName == "Start")
+			{
+				LinkedPreview.Start = Start;
+				LinkedPreview?.Update(Time, 0, false);
+			}
+			if (e.PropertyName == "PivotX")
+			{
+				LinkedPreview.PivotX = PivotX;
+			}
+			else if (e.PropertyName == "PivotY")
+			{
+				LinkedPreview.PivotY = PivotY;
+			}
+			else if (e.PropertyName == "ParentId")
+			{
+				LinkedPreview.ParentId = ParentId;
+			}
+			else if (e.PropertyName == "Length")
+			{
+				((LiveAnimatedObject)LinkedPreview).Length = Length;
+			}
+			OnSourcePropertyChanged(sender, e);
+		}
+		protected virtual void OnSourcePropertyChanged(object sender, PropertyChangedEventArgs e)
+		{
+		}
+
+		private void Source_KeyframesChanged(object sender, NotifyCollectionChangedEventArgs e)
+		{
+			InvalidatePreview();
+		}
+
+		public override void UpdateRealTime(float deltaTime, bool inPlayback) { }
+
+		public sealed override void Update(float time, float elapsedTime, bool inPlayback)
+		{
+			Time = time;
+			if (inPlayback)
+			{
+				if (_lastPlaybackTime != time)
+				{
+					TimeOffset = 0;
+				}
+				else
+				{
+					TimeOffset += (elapsedTime - _lastElapsedTime);
+				}
+				_lastElapsedTime = elapsedTime;
+				_lastPlaybackTime = time;
+			}
+
+			string easeOverride = (inPlayback ? null : "linear");
+			string interpolationOverride = (inPlayback ? null : "linear");
+			bool? loopOverride = (inPlayback ? null : new bool?(false));
+
+			OnUpdate(time, TimeOffset, easeOverride, interpolationOverride, loopOverride, inPlayback);
+			OnUpdateDimensions();
+		}
+		protected virtual void OnUpdate(float time, float offset, string ease, string interpolation, bool? looped, bool inPlayback)
+		{
+
+		}
+		protected virtual void OnUpdateDimensions()
+		{
+			int newWidth = Width;
+			int newHeight = Height;
+			if (Image != null)
+			{
+				newWidth = Image.Width;
+				newHeight = Image.Height;
+			}
+			else
+			{
+				newWidth = 100;
+				newHeight = 100;
+			}
+			if (newWidth != Width || newHeight != Height)
+			{
+				Width = newWidth;
+				Height = newHeight;
+				UpdateLocalTransform();
+			}
+		}
+
+		/// <summary>
+		/// Adds a keyframe from a definition
+		/// </summary>
+		/// <param name="kf"></param>
+		public HashSet<string> AddKeyframe(Keyframe kf, float timeOffset, bool addBreak, out LiveKeyframe frame)
+		{
+			HashSet<string> properties = new HashSet<string>();
+
+			float time;
+			float.TryParse(kf.Time, NumberStyles.Number, CultureInfo.InvariantCulture, out time);
+			time += timeOffset;
+
+			ParseKeyframe(kf, addBreak, properties, time);
+
+			frame = Keyframes.Find(k => k.Time == time);
+			return properties;
+		}
+
+		protected virtual void ParseKeyframe(Keyframe kf, bool addBreak, HashSet<string> properties, float time)
+		{
+		}
+
+		/// <summary>
+		/// Merges a directive into this preview to have one single animation
+		/// </summary>
+		/// <param name="directive"></param>
+		public void AddKeyframeDirective(Directive directive, float offset)
+		{
+			float delay = Start;
+			if (!string.IsNullOrEmpty(directive.Delay))
+			{
+				float.TryParse(directive.Delay, NumberStyles.Number, CultureInfo.InvariantCulture, out delay);
+			}
+			float startTime = delay - Start + offset;
+			if (startTime < 0)
+			{
+				startTime = 0; //if the delay was shorter than the sprite's delay, use no delay at all. This setup wouldn't work well anyway.
+			}
+
+			HashSet<string> affectedProperties = new HashSet<string>();
+			directive.Keyframes.Sort((k1, k2) =>
+			{
+				string t1 = k1.Time ?? "0";
+				string t2 = k2.Time ?? "0";
+				return t1.CompareTo(t2);
+			});
+			bool frameExists = Keyframes.Find(k => k.Time == startTime) != null;
+			for (int i = 0; i < directive.Keyframes.Count; i++)
+			{
+				Keyframe kf = directive.Keyframes[i];
+				bool addBreak = (i == 0 && startTime > 0);
+				LiveKeyframe liveFrame;
+				HashSet<string> properties = AddKeyframe(kf, startTime, addBreak, out liveFrame);
+
+				foreach (string prop in properties)
+				{
+					affectedProperties.Add(prop);
+				}
+			}
+
+			LiveKeyframe startFrame = Keyframes.Find(kf => kf.Time == startTime);
+			foreach (string prop in affectedProperties)
+			{
+				if (startFrame != null)
+				{
+					LiveKeyframeMetadata metadata = startFrame.GetMetadata(prop, true);
+					string ease = directive.EasingMethod ?? "linear";
+					string interpolation = directive.InterpolationMethod ?? "none";
+					bool looped = directive.Looped;
+					string clamp = directive.ClampingMethod;
+					int iterations = directive.Iterations;
+
+					metadata.Ease = ease;
+					metadata.Interpolation = interpolation;
+					metadata.Looped = looped;
+					metadata.ClampMethod = clamp;
+					metadata.Iterations = iterations;
+				}
+			}
+		}
+
+		/// <summary>
+		/// Gets the next frame containing a property after the frame at index
+		/// </summary>
+		/// <param name="index"></param>
+		/// <param name="property"></param>
+		/// <returns></returns>
+		private LiveKeyframe GetNextFrame(int index, string property)
+		{
+			for (int j = index + 1; j < Keyframes.Count; j++)
+			{
+				LiveKeyframe kf = Keyframes[j];
+				if (kf.HasProperty(property))
+				{
+					return kf;
+				}
+			}
+			return null;
+		}
+
+		/// <summary>
+		/// Retrieves the beginning and ending frames of animation block
+		/// </summary>
+		/// <param name="property">Property being animated</param>
+		/// <param name="time">Time to find which block it belongs to</param>
+		/// <param name="start">Outputs start frame</param>
+		/// <param name="end">Outputs end frame</param>
+		public virtual void GetBlock(string property, float time, out LiveKeyframe start, out LiveKeyframe end)
+		{
+			start = null;
+			end = null;
+			LiveKeyframe first = null;
+			LiveKeyframe last = null;
+			for (int i = 0; i < Keyframes.Count; i++)
+			{
+				LiveKeyframe kf = Keyframes[i];
+				if (!kf.HasProperty(property)) { continue; }
+
+				if (first == null)
+				{
+					first = kf;
+				}
+				last = kf;
+
+				KeyframeType type = i == 0 ? KeyframeType.Begin : kf.GetFrameType(property);
+				if (start == null)
+				{
+					if (kf.Time <= time && (i == 0 || type == KeyframeType.Begin || type == KeyframeType.Split))
+					{
+						//found a possible starting point
+						start = kf;
+					}
+					else if (first == kf && kf.Time > time)
+					{
+						//first frame for a particular property is considered start point if time is before that frame
+						start = kf;
+					}
+				}
+				else
+				{
+					LiveKeyframe next = GetNextFrame(i, property);
+					KeyframeType nextType = next == null ? KeyframeType.Begin : next.GetFrameType(property);
+
+					if (type == KeyframeType.Begin)
+					{
+						//this frame is a begin, so use the last frame as the end if time is in range
+						//this should only be entered if the previous frame was the start. Otherwise the nextType == Begin below would cover this case a step earlier
+						if (time <= kf.Time)
+						{
+							end = start;
+							return;
+						}
+						else
+						{
+							//otherwise use this as the new start
+							last = null;
+							start = kf;
+						}
+					}
+					else if (type == KeyframeType.Split)
+					{
+						//this frame is a split, so if time is in range, use this frame as the end
+						if (time < kf.Time)
+						{
+							end = kf;
+							return;
+						}
+						else
+						{
+							//otherwise use this as the new start
+							last = null;
+							start = kf;
+						}
+					}
+					else if (nextType == KeyframeType.Begin)
+					{
+						//next frame begins a new set, so if time is before that, use this frame as the end
+						if (time < kf.Time)
+						{
+							end = last;
+							return;
+						}
+						else if (next == null)
+						{
+							//no more keyframes, so this must be the end
+							end = kf;
+							return;
+						}
+						else
+						{
+							//otherwise this range doesn't contain the frame
+							last = null;
+							start = null;
+						}
+					}
+				}
+			}
+			end = (last == null ? start : last);
+		}
+
+		private bool HasLoops()
+		{
+			for (int i = 0; i < Keyframes.Count; i++)
+			{
+				LiveKeyframe kf = Keyframes[i];
+				foreach (string property in kf.TrackedProperties)
+				{
+					if (!kf.HasProperty(property)) { continue; }
+
+					LiveKeyframeMetadata metadata = kf.GetMetadata(property, false);
+					if (metadata != null && metadata.Looped)
+					{
+						return true;
+					}
+				}
+			}
+			return false;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveBreak.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveBreak.cs
new file mode 100644
index 0000000000000000000000000000000000000000..deddfd2586c88ce93fc5db4ce0a0e4e8949d3827
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveBreak.cs	
@@ -0,0 +1,176 @@
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+using Desktop.DataStructures;
+using Desktop.Skinning;
+using SPNATI_Character_Editor.Actions;
+using SPNATI_Character_Editor.Actions.TimelineActions;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class LiveBreak : BindableObject, ITimelineBreak
+	{
+		public ITimelineData Data;
+
+		public object GetData()
+		{
+			return this;
+		}
+
+		/// <summary>
+		/// Background width in seconds
+		/// </summary>
+		private const float BackgroundWidth = 0.25f;
+
+		private float _lastWidth;
+		private LinearGradientBrush _brushBackground;
+		private static Pen _penOuter;
+		private static Pen _penInner;
+		private static Pen _penSelection;
+
+		static LiveBreak()
+		{
+			_penSelection = new Pen(Color.White);
+			_penOuter = new Pen(Color.White, 3);
+			_penInner = new Pen(Color.Black);
+		}
+
+		public LiveBreak()
+		{
+			UpdateSkin(SkinManager.Instance.CurrentSkin);
+		}
+
+		public static void UpdateColors(Skin skin)
+		{
+			_penInner.Color = Color.Gray;
+			_penOuter.Color = Color.White;
+			_penSelection.Color = Color.FromArgb(0, 165, 255);
+		}
+
+		public void UpdateSkin(Skin skin)
+		{
+			_lastWidth = -1;
+			UpdateColors(skin);
+		}
+
+		public float Time
+		{
+			get { return Get<float>(); }
+			set { Set(value); _lastWidth = -1; }
+		}
+
+		public void DrawBackground(Graphics g, float pps, int height, bool selected)
+		{
+			float x = Time * pps;
+			float width = BackgroundWidth * pps;
+			float left = x - width;
+
+			if (width != _lastWidth)
+			{
+				_brushBackground?.Dispose();
+				_brushBackground = new LinearGradientBrush(new PointF(left, 0), new PointF(x, 0), Color.Transparent, Color.FromArgb(200, selected ? _penSelection.Color : _penOuter.Color));
+				_lastWidth = width;
+			}
+
+			g.FillRectangle(_brushBackground, left, 0, width, height);
+		}
+
+		public void Draw(Graphics g, float pps, int height, bool selected)
+		{
+			float x = Time * pps;
+			g.DrawLine(_penOuter, x, 0, x, height);
+			g.DrawLine(_penInner, x, 0, x, height);
+			if (selected)
+			{
+				g.DrawRectangle(_penSelection, x - 2, 0, 4, height - 1);
+			}
+		}
+
+		public override string ToString()
+		{
+			return "Wait for input";
+		}
+
+		public float GetStart()
+		{
+			return Time;
+		}
+
+		public void SetStart(float time)
+		{
+			Time = time;
+		}
+
+		public void OnWidgetSelectionChanged(WidgetSelectionArgs args)
+		{
+			_lastWidth = -1;
+			if (args.IsSelected == SelectionType.Select)
+			{
+				args.Timeline.SelectData(this);
+			}
+		}
+
+		public ITimelineAction GetAction(int x, float start, int row, int timelineWidth, float pps)
+		{
+			return new MoveWidgetTimelineAction(false);
+		}
+
+		public void UpdateSelection(WidgetSelectionArgs args)
+		{
+			args.AllowDelete = true;
+		}
+
+		public bool OnCopy(WidgetOperationArgs args)
+		{
+			return false;
+		}
+
+		public bool OnDelete(WidgetOperationArgs args)
+		{
+			if (args.IsSilent || MessageBox.Show($"Are you sure you want to completely remove {ToString()}?", "Remove Sprite", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
+			{
+				DeleteWidgetCommand command = new DeleteWidgetCommand(Data, this);
+				args.History.Commit(command);
+				args.Timeline.SelectData(null);
+			}
+			return true;
+		}
+
+		public bool OnPaste(WidgetOperationArgs args)
+		{
+			return false;
+		}
+
+		public bool OnDuplicate(WidgetOperationArgs args)
+		{
+			return false;
+		}
+
+		public void OnOpeningContextMenu(ContextMenuArgs args)
+		{
+
+		}
+
+		public void AdvanceSubWidget(bool forward)
+		{
+			//go to the next break
+		}
+
+		public void OnTimeChanged(WidgetOperationArgs args)
+		{
+		}
+
+		public void OnDoubleClick(WidgetActionArgs args)
+		{
+		}
+
+		public void OnStartMove(WidgetActionArgs args)
+		{
+
+		}
+
+		public void OnMouseOut()
+		{
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveBubble.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveBubble.cs
new file mode 100644
index 0000000000000000000000000000000000000000..16ddd163439ce064b3199098c0b919c518ced207
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveBubble.cs	
@@ -0,0 +1,564 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Globalization;
+using System.Xml.Serialization;
+using Desktop.CommonControls.PropertyControls;
+using SPNATI_Character_Editor.Controls;
+using SPNATI_Character_Editor.Controls.EditControls;
+using SPNATI_Character_Editor.EditControls;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	/// <summary>
+	/// Editable speech bubble
+	/// </summary>
+	public class LiveBubble : LiveAnimatedObject
+	{
+		private const int Padding = 5;
+
+		private static Font _font;
+		private static StringFormat _stringFormat;
+		private static Graphics _graphics;
+		private static Pen _alignmentPen;
+
+		static LiveBubble()
+		{
+			_font = new Font("Trebuchet MS", 14);
+			_stringFormat = new StringFormat() { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center };
+			_graphics = Graphics.FromImage(new Bitmap(1, 1));
+			_alignmentPen = new Pen(Color.FromArgb(127, 255, 255, 255), 2);
+		}
+
+		public LiveBubble()
+		{
+			DisplayPastEnd = false;
+			CenterX = false;
+		}
+		public LiveBubble(LiveData data, float time) : this()
+		{
+			Data = data;
+			Length = 1;
+			Start = time;
+			LiveBubbleKeyframe startFrame = CreateKeyframe(0) as LiveBubbleKeyframe;
+			startFrame.Text = "New text";
+			TextWidth = "20%";
+			AddKeyframe(startFrame);
+			Update(time, 0, false);
+			UpdateLocalTransform();
+		}
+
+		public LiveBubble(LiveData data, Directive directive, float time) : this()
+		{
+			DisplayPastEnd = false;
+			Data = data;
+			Length = 1;
+
+			TextX = directive.X;
+			TextY = directive.Y;
+			TextWidth = directive.Width;
+			Id = directive.Id;
+
+			LiveKeyframe temp;
+			AddKeyframe(directive, 0, false, out temp);
+			Update(time, 0, false);
+		}
+
+		public string Text
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[Measurement(DisplayName = "X", Key = "x", GroupOrder = 5, Description = "Speech bubble left position to the screen width")]
+		public string TextX
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[Measurement(DisplayName = "Y", Key = "y", GroupOrder = 10, Description = "Speech bubble top position to the screen height")]
+		public string TextY
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[Measurement(DisplayName = "Width", Key = "width", GroupOrder = 20, Description = "Speech bubble width relative to the screen width")]
+		public string TextWidth
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[ComboBox(DisplayName = "Arrow", Key = "arrow", GroupOrder = 30, Description = "Speech bubble arrow direction", Options = new string[] { "down", "up", "left", "right" })]
+		public string Arrow
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[HorizontalAlignment(DisplayName = "X Alignment", Key = "alignmentx", GroupOrder = 31, Description = "Horizontal alignment")]
+		public string AlignmentX
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[VerticalAlignment(DisplayName = "Y Alignment", Key = "alignmenty", GroupOrder = 32, Description = "Vertical alignment")]
+		[XmlAttribute("alignmenty")]
+		public string AlignmentY
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		protected override void OnUpdateDimensions() { }
+
+		public override Type GetKeyframeType()
+		{
+			return typeof(LiveBubbleKeyframe);
+		}
+
+		public override ITimelineWidget CreateWidget(Timeline timeline)
+		{
+			return new TextWidget(this, timeline);
+		}
+
+		protected override void ParseKeyframe(Keyframe kf, bool addBreak, HashSet<string> properties, float time)
+		{
+			Directive dir = kf as Directive;
+			if (dir != null)
+			{
+				if (!string.IsNullOrEmpty(dir.Text))
+				{
+					AddValue<string>(time, "Text", dir.Text);
+					properties.Add("Text");
+				}
+			}
+		}
+
+		protected override void OnUpdate(float time, float offset, string ease, string interpolation, bool? looped, bool inPlayback)
+		{
+			Text = GetPropertyValue<string>("Text", time, offset, null, "linear", "none", false);
+		}
+
+		public bool Contains(Point pt, Matrix sceneTransform)
+		{
+			RectangleF rect = GetRectangle();
+			return rect.Contains(pt);
+		}
+
+		private RectangleF GetRectangle()
+		{
+			LiveScene scene = Data as LiveScene;
+			float width = EpilogueEditing.SceneObject.Parse(TextWidth, scene.Width);
+			float x = EpilogueEditing.SceneObject.Parse(TextX, scene.Width);
+			if (TextX == "centered")
+			{
+				x = scene.Width / 2 - width / 2;
+			}
+			float y = EpilogueEditing.SceneObject.Parse(TextY, scene.Height);
+
+			SizeF size = _graphics.MeasureString(Text, _font, (int)(width - Padding * 2), _stringFormat);
+			float height = size.Height + Padding * 2;
+
+			X = x;
+			Y = y;
+
+			if (!string.IsNullOrEmpty(AlignmentX) && AlignmentX != "left" && TextX != "centered")
+			{
+				if (AlignmentX == "center")
+				{
+					x -= width / 2;
+				}
+				else if (AlignmentX == "right")
+				{
+					x -= width;
+				}
+			}
+			if (!string.IsNullOrEmpty(AlignmentY) && AlignmentY != "top")
+			{
+				if (AlignmentY == "center")
+				{
+					y -= height / 2;
+				}
+				else if (AlignmentY == "bottom")
+				{
+					y -= height;
+				}
+			}
+
+			Width = (int)width;
+			Height = (int)height;
+
+			return new RectangleF(x, y, width, height);
+		}
+
+		public override void Draw(Graphics g, Matrix sceneTransform, List<string> markers, bool inPlayback)
+		{
+			if (!IsVisible || Hidden) { return; }
+			if (HiddenByMarker(markers))
+			{
+				return;
+			}
+
+			RectangleF bounds = GetRectangle();
+
+			//alignment grid
+			if (!inPlayback)
+			{
+				float gridX = bounds.X;
+				if (AlignmentX == "center")
+				{
+					gridX = bounds.X + bounds.Width / 2;
+				}
+				else if (AlignmentX == "right")
+				{
+					gridX = bounds.Right;
+				}
+				float gridY = bounds.Y;
+				if (AlignmentY == "center")
+				{
+					gridY = bounds.Y + bounds.Height / 2;
+				}
+				else if (AlignmentY == "bottom")
+				{
+					gridY = bounds.Bottom;
+				}
+				g.DrawLine(_alignmentPen, bounds.X - 20, gridY, bounds.Right + 20, gridY);
+				g.DrawLine(_alignmentPen, gridX, bounds.Y - 20, gridX, bounds.Bottom + 20);
+			}
+
+			g.FillRectangle(Brushes.White, bounds);
+			g.DrawString(Text, _font, Brushes.Black, new RectangleF(bounds.X, bounds.Y + Padding, bounds.Width, bounds.Height - Padding * 2), _stringFormat);
+			g.DrawRectangle(Pens.Black, bounds.X, bounds.Y, bounds.Width, bounds.Height);
+
+			if (!string.IsNullOrEmpty(Arrow))
+			{
+				DrawArrow(g, bounds, Arrow, true);
+			}
+		}
+
+		private void DrawArrow(Graphics g, RectangleF bounds, string side, bool opaque)
+		{
+			using (SolidBrush fillBrush = new SolidBrush(opaque ? Color.White : Color.FromArgb(127, Color.White)))
+			{
+				const int ArrowSize = 16;
+				if (side == "down")
+				{
+					float center = bounds.X + bounds.Width / 2;
+					float y = bounds.Y + bounds.Height - 1;
+					PointF p1 = new PointF(center - ArrowSize, y);
+					PointF p2 = new PointF(center + ArrowSize, y);
+					PointF p3 = new PointF(center, y + ArrowSize);
+					PointF[] triangle = new PointF[] { p1, p2, p3 };
+					g.FillPolygon(fillBrush, triangle);
+					g.DrawLine(Pens.Black, p1, p3);
+					g.DrawLine(Pens.Black, p2, p3);
+				}
+				if (side == "up")
+				{
+					float center = bounds.X + bounds.Width / 2;
+					float y = bounds.Y + 1;
+					PointF p1 = new PointF(center + ArrowSize, y);
+					PointF p2 = new PointF(center - ArrowSize, y);
+					PointF p3 = new PointF(center, y - ArrowSize);
+					PointF[] triangle = new PointF[] { p1, p2, p3 };
+					g.FillPolygon(fillBrush, triangle);
+					g.DrawLine(Pens.Black, p1, p3);
+					g.DrawLine(Pens.Black, p2, p3);
+				}
+				if (side == "left")
+				{
+					float center = bounds.Y + bounds.Height / 2;
+					float x = bounds.X + 1;
+					PointF p1 = new PointF(x, center - ArrowSize);
+					PointF p2 = new PointF(x, center + ArrowSize);
+					PointF p3 = new PointF(x - ArrowSize, center);
+					PointF[] triangle = new PointF[] { p1, p2, p3 };
+					g.FillPolygon(fillBrush, triangle);
+					g.DrawLine(Pens.Black, p1, p3);
+					g.DrawLine(Pens.Black, p2, p3);
+				}
+				if (side == "right")
+				{
+					float center = bounds.Y + bounds.Height / 2;
+					float x = bounds.X + bounds.Width - 1;
+					PointF p1 = new PointF(x, center + ArrowSize);
+					PointF p2 = new PointF(x, center - ArrowSize);
+					PointF p3 = new PointF(x + ArrowSize, center);
+					PointF[] triangle = new PointF[] { p1, p2, p3 };
+					g.FillPolygon(fillBrush, triangle);
+					g.DrawLine(Pens.Black, p1, p3);
+					g.DrawLine(Pens.Black, p2, p3);
+				}
+			}
+		}
+
+		public override void DrawSelection(Graphics g, Matrix sceneTransform, CanvasState editState, HoverContext hoverContext)
+		{
+			LiveScene scene = Data as LiveScene;
+			if (scene != null)
+			{
+				g.MultiplyTransform(scene.Camera.WorldTransform);
+				g.MultiplyTransform(sceneTransform, MatrixOrder.Append);
+				RectangleF rect = GetRectangle();
+				DrawSelectionBox(g,
+					new PointF(rect.Left, rect.Top),
+					new PointF(rect.Right, rect.Top),
+					new PointF(rect.Right, rect.Bottom),
+					new PointF(rect.Left, rect.Bottom)
+				);
+				DrawHandles(g,
+					new PointF(rect.Left, rect.Top),
+					new PointF(rect.Left, rect.Bottom));
+				DrawHandles(g,
+					new PointF(rect.Right, rect.Top),
+					new PointF(rect.Right, rect.Bottom));
+
+				string arrowPreview = null;
+				switch (hoverContext)
+				{
+					case HoverContext.ArrowDown:
+						arrowPreview = "down";
+						break;
+					case HoverContext.ArrowLeft:
+						arrowPreview = "left";
+						break;
+					case HoverContext.ArrowRight:
+						arrowPreview = "right";
+						break;
+					case HoverContext.ArrowUp:
+						arrowPreview = "up";
+						break;
+				}
+				if (!string.IsNullOrEmpty(arrowPreview))
+				{
+					DrawArrow(g, GetRectangle(), arrowPreview, false);
+				}
+				g.ResetTransform();
+			}
+		}
+
+		public override PointF[] ToLocalPt(Matrix sceneTransform, params PointF[] pts)
+		{
+			LiveScene scene = Data as LiveScene;
+			Matrix transform = new Matrix();
+			transform.Multiply(scene.Camera.WorldTransform);
+			transform.Multiply(sceneTransform, MatrixOrder.Append);
+			transform.Invert();
+			RectangleF rect = GetRectangle(); //bubble in camera space
+
+			//convert points to camera space
+			transform.TransformPoints(pts);
+
+			//difference is local space
+			for (int i = 0; i < pts.Length; i++)
+			{
+				pts[i] = new PointF(pts[i].X - rect.X, pts[i].Y - rect.Y);
+			}
+			return pts;
+		}
+
+		public override PointF[] ToScreenPt(Matrix sceneTransform, params PointF[] pts)
+		{
+			LiveScene scene = Data as LiveScene;
+			Matrix transform = new Matrix();
+			transform.Multiply(scene.Camera.WorldTransform);
+			transform.Multiply(sceneTransform, MatrixOrder.Append);
+			RectangleF rect = GetRectangle();
+
+			for (int i = 0; i < pts.Length; i++)
+			{
+				pts[i] = new PointF(pts[i].X + rect.X, pts[i].Y + rect.Y);
+			}
+
+			transform.TransformPoints(pts);
+
+			return pts;
+		}
+
+		public override PointF[] ToWorldPt(params PointF[] pts)
+		{
+			LiveScene scene = Data as LiveScene;
+			for (int i = 0; i < pts.Length; i++)
+			{
+				pts[i] = new PointF(pts[i].X / scene.Width * 100, pts[i].Y / scene.Height * 100);
+			}
+			return pts;
+		}
+
+		public override PointF[] ScreenToWorldPt(Matrix sceneTransform, params PointF[] pts)
+		{
+			LiveScene scene = Data as LiveScene;
+			Matrix transform = new Matrix();
+			transform.Multiply(scene.Camera.WorldTransform);
+			transform.Multiply(sceneTransform, MatrixOrder.Append);
+			transform.Invert();
+
+			//convert points to camera space, which is world space for text boxes
+			transform.TransformPoints(pts);
+
+			//but as a percentage of the scene
+			for (int i = 0; i < pts.Length; i++)
+			{
+				pts[i] = new PointF(pts[i].X / scene.Width * 100, pts[i].Y / scene.Height * 100);
+			}
+
+			return pts;
+		}
+
+		protected override void OnPreviewPropertyChanged(object sender, PropertyChangedEventArgs e)
+		{
+			LiveBubble preview = LinkedPreview as LiveBubble;
+			if (e.PropertyName == "TextX")
+			{
+				TextX = preview.TextX;
+			}
+			else if (e.PropertyName == "TextY")
+			{
+				TextY = preview.TextY;
+			}
+			else if (e.PropertyName == "Arrow")
+			{
+				Arrow = preview.Arrow;
+			}
+			else if (e.PropertyName == "TextWidth")
+			{
+				TextWidth = preview.TextWidth;
+			}
+		}
+
+		protected override void OnSourcePropertyChanged(object sender, PropertyChangedEventArgs e)
+		{
+			LiveBubble preview = LinkedPreview as LiveBubble;
+			if (e.PropertyName == "TextX")
+			{
+				preview.TextX = TextX;
+			}
+			else if (e.PropertyName == "TextY")
+			{
+				preview.TextY = TextY;
+			}
+			else if (e.PropertyName == "Arrow")
+			{
+				preview.Arrow = Arrow;
+			}
+			else if (e.PropertyName == "TextWidth")
+			{
+				preview.TextWidth = TextWidth;
+			}
+			else if (e.PropertyName == "AlignmentX")
+			{
+				preview.AlignmentX = AlignmentX;
+			}
+			else if (e.PropertyName == "AlignmentY")
+			{
+				preview.AlignmentY = AlignmentY;
+			}
+		}
+
+		public override bool CanPivot { get { return false; } }
+		public override bool CanRotate { get { return false; } }
+		public override bool CanSkew { get { return false; } }
+		public override bool CanArrow { get { return true; } }
+
+		public override void Scale(Point screenPoint, Matrix sceneTransform, HoverContext context)
+		{
+			LiveScene scene = Data as LiveScene;
+			RectangleF rect = GetRectangle();
+			switch (context)
+			{
+				case HoverContext.ScaleRight:
+					PointF localPt = ToLocalPt(screenPoint.X, screenPoint.Y, sceneTransform);
+					int width = (int)Math.Round(localPt.X / scene.Width * 100);
+					string newWidth = Math.Max(0, width) + "%";
+					TextWidth = newWidth;
+					break;
+				case HoverContext.ScaleLeft:
+					localPt = ToLocalPt(screenPoint.X, screenPoint.Y, sceneTransform);
+					width = (int)Math.Round((rect.Width - localPt.X) / scene.Width * 100);
+					int oldW = (int)Math.Max(0, rect.Width / scene.Width * 100);
+					//update left position as well so the right doesn't move
+					int left = (int)(Math.Round(localPt.X + rect.X) / scene.Width * 100);
+					int oldLeft = (int)(Math.Round(rect.X / scene.Width * 100));
+					int diff = oldLeft - left;
+					newWidth = (oldW + diff) + "%";
+					TextX = left + "%";
+					TextWidth = newWidth;
+					break;
+			}
+		}
+
+		public override void Translate(float x, float y, Matrix sceneTransform)
+		{
+			TextX = Math.Round(x).ToString(CultureInfo.InvariantCulture) + "%";
+			TextY = Math.Round(y).ToString(CultureInfo.InvariantCulture) + "%";
+		}
+
+		public override void UpdateArrowPosition(HoverContext arrowContext)
+		{
+			string position = "";
+			switch (arrowContext)
+			{
+				case HoverContext.ArrowDown:
+					position = "down";
+					break;
+				case HoverContext.ArrowLeft:
+					position = "left";
+					break;
+				case HoverContext.ArrowRight:
+					position = "right";
+					break;
+				case HoverContext.ArrowUp:
+					position = "up";
+					break;
+			}
+			Arrow = position;
+		}
+
+		public override bool FilterRecord(string key)
+		{
+			switch (key)
+			{
+				case "pivotx":
+				case "pivoty":
+				case "z":
+					return false;
+				default:
+					return true;
+			}
+		}
+
+		public override void GetBlock(string property, float time, out LiveKeyframe start, out LiveKeyframe end)
+		{
+			start = null;
+			end = null;
+			if (Keyframes.Count == 0)
+			{
+				return;
+			}
+			for (int i = 0; i < Keyframes.Count; i++)
+			{
+				LiveKeyframe kf = Keyframes[i];
+				if (kf.Time <= time)
+				{
+					start = kf;
+				}
+				else if (kf.Time > time)
+				{
+					if (start == null)
+					{
+						start = kf;
+					}
+					end = kf;
+					return;
+				}
+			}
+			end = start = Keyframes[0];
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveBubbleKeyframe.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveBubbleKeyframe.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f34bdb910f6a2db74a2971ce4dbf3b73eecb0b97
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveBubbleKeyframe.cs	
@@ -0,0 +1,34 @@
+using Desktop;
+using Desktop.CommonControls.PropertyControls;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class LiveBubbleKeyframe : LiveKeyframe
+	{
+		[Text(DisplayName = "Text", Key = "text", GroupOrder = 5, Description = "Speech bubble text", RowHeight = 52, Multiline = true)]
+		public string Text
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		public LiveBubbleKeyframe() : base()
+		{
+			TrackedProperties.Remove("X");
+			TrackedProperties.Remove("Y");
+			TrackedProperties.Add("Text");
+		}
+
+		public override bool FilterRecord(PropertyRecord record)
+		{
+			switch (record.Key)
+			{
+				case "x":
+				case "y":
+					return false;
+				default:
+					return base.FilterRecord(record);
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveCamera.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveCamera.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8a133a1a7b40b34f8f687bdda01c364172a4e12e
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveCamera.cs	
@@ -0,0 +1,290 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Globalization;
+using SPNATI_Character_Editor.Controls;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class LiveCamera : LiveAnimatedObject
+	{
+		public float Zoom
+		{
+			get { return Get<float>(); }
+			set
+			{
+				value = (float)Math.Round(value, 2);
+				if (value <= 0)
+				{
+					value = 0.01f;
+				}
+				Set(value);
+				UpdateLocalTransform();
+			}
+		}
+
+		public Color Color
+		{
+			get { return Get<Color>(); }
+			set { Set(value); }
+		}
+
+		public bool BlockOutsideView
+		{
+			get; set;
+		}
+
+		private Pen _penLens;
+		private Brush _outsideBrush;
+
+		public LiveCamera()
+		{
+			Zoom = 1;
+			Color = Color.Black;
+			Alpha = 0;
+			Length = 1;
+
+			_penLens = new Pen(Brushes.LightGray, 5);
+			_penLens.Color = Color.FromArgb(127, _penLens.Color);
+
+			_outsideBrush = new SolidBrush(Color.FromArgb(80, 0, 10, 30));
+			AddValue<float>(0, "Zoom", "1");
+		}
+
+		public LiveCamera(Scene scene) : this()
+		{
+			if (!string.IsNullOrEmpty(scene.Width))
+			{
+				int width;
+				if (int.TryParse(scene.Width, NumberStyles.Number, CultureInfo.InvariantCulture, out width))
+				{
+					Width = width;
+				}
+			}
+			if (!string.IsNullOrEmpty(scene.Height))
+			{
+				int height;
+				if (int.TryParse(scene.Height, NumberStyles.Number, CultureInfo.InvariantCulture, out height))
+				{
+					Height = height;
+				}
+			}
+			Id = "Camera";
+			AddValue<float>(0, "X", scene.X);
+			AddValue<float>(0, "Y", scene.Y);
+			AddValue<float>(0, "Zoom", scene.Zoom);
+			AddValue<float>(0, "Alpha", scene.FadeOpacity);
+			AddValue<Color>(0, "Color", scene.FadeColor);
+			Update(0, 0, false);
+		}
+
+		protected override void OnCopyTo(LiveObject copy)
+		{
+			copy.Width = Width;
+			copy.Height = Height;
+			LiveCamera cameraCopy = copy as LiveCamera;
+			cameraCopy.BlockOutsideView = BlockOutsideView;
+		}
+
+		public override Type GetKeyframeType()
+		{
+			return typeof(LiveCameraKeyframe);
+		}
+
+		protected override void ParseKeyframe(Keyframe kf, bool addBreak, HashSet<string> properties, float time)
+		{
+			if (!string.IsNullOrEmpty(kf.X))
+			{
+				AddValue<float>(time, "X", kf.X, addBreak);
+				properties.Add("X");
+			}
+			if (!string.IsNullOrEmpty(kf.Y))
+			{
+				AddValue<float>(time, "Y", kf.Y, addBreak);
+				properties.Add("Y");
+			}
+			if (!string.IsNullOrEmpty(kf.Zoom))
+			{
+				AddValue<float>(time, "Zoom", kf.Zoom, addBreak);
+				properties.Add("Zoom");
+			}
+			if (!string.IsNullOrEmpty(kf.Opacity))
+			{
+				AddValue<float>(time, "Alpha", kf.Opacity, addBreak);
+				properties.Add("Alpha");
+			}
+			if (!string.IsNullOrEmpty(kf.Color))
+			{
+				AddValue<Color>(time, "Color", kf.Color, addBreak);
+				properties.Add("Color");
+			}
+		}
+
+		protected override void OnUpdateDimensions() { }
+
+		public override ITimelineWidget CreateWidget(Timeline timeline)
+		{
+			return new CameraWidget(this, timeline);
+		}
+
+		protected override void OnUpdate(float time, float offset, string ease, string interpolation, bool? looped, bool inPlayback)
+		{
+			X = GetPropertyValue("X", time, offset, 0.0f, ease, interpolation, looped);
+			Y = GetPropertyValue("Y", time, offset, 0.0f, ease, interpolation, looped);
+			Zoom = GetPropertyValue("Zoom", time, offset, 1.0f, ease, interpolation, looped);
+			Alpha = GetPropertyValue("Alpha", time, offset, 0.0f, ease, interpolation, looped);
+			Color = GetPropertyValue("Color", time, offset, Color.Black, ease, interpolation, looped);
+		}
+
+		protected override void UpdateLocalTransform()
+		{
+			Matrix transform = new Matrix();
+			float pivotX = 0.5f * Width;
+			float pivotY = 0.5f * Height;
+			transform.Translate(-pivotX, -pivotY, MatrixOrder.Append);
+			transform.Scale(1 / Zoom, 1 / Zoom, MatrixOrder.Append);
+			transform.Translate(pivotX, pivotY, MatrixOrder.Append);
+
+			transform.Translate(X, Y, MatrixOrder.Append); //local position
+			LocalTransform = transform;
+		}
+
+		public override void Draw(Graphics g, Matrix sceneTransform, List<string> markers, bool inPlayback)
+		{
+			PointF[] bounds = ToScreenPt(sceneTransform, new PointF(0, 0), new PointF(Width, Height));
+
+			PointF tl = bounds[0];
+			PointF br = new PointF(bounds[1].X, bounds[1].Y);
+			PointF tr = new PointF(bounds[1].X, bounds[0].Y);
+			PointF bl = new PointF(bounds[0].X, bounds[1].Y);
+
+			int width = (int)g.ClipBounds.Width;
+			int height = (int)g.ClipBounds.Height;
+
+			Brush outerBrush = BlockOutsideView ? Brushes.Black : _outsideBrush;
+			g.FillRectangle(outerBrush, 0, 0, bounds[0].X, height);
+			g.FillRectangle(outerBrush, bounds[1].X, 0, width - bounds[1].X, height);
+			g.FillRectangle(outerBrush, bounds[0].X, 0, bounds[1].X - bounds[0].X, bounds[0].Y);
+			g.FillRectangle(outerBrush, bounds[0].X, bounds[1].Y, bounds[1].X - bounds[0].X, height - bounds[1].Y);
+
+			//Fade overlay
+			if (Alpha > 0)
+			{
+				using (SolidBrush overlay = new SolidBrush(Color.FromArgb((int)(Alpha / 100.0 * 255), Color)))
+				{
+					g.FillRectangle(overlay, 0, 0, width, height);
+				}
+			}
+
+			if (!BlockOutsideView)
+			{
+				g.DrawLine(Pens.Gray, tl, bl);
+				g.DrawLine(Pens.Gray, tl, tr);
+				g.DrawLine(Pens.Gray, tr, br);
+				g.DrawLine(Pens.Gray, bl, br);
+
+				//corners
+				const int CornerSize = 50;
+				float halfWidth = _penLens.Width / 2;
+				g.DrawLine(_penLens, new PointF(tl.X, tl.Y - halfWidth), new PointF(tl.X - CornerSize, tl.Y - halfWidth));
+				g.DrawLine(_penLens, new PointF(tl.X - halfWidth, tl.Y), new PointF(tl.X - halfWidth, tl.Y - CornerSize));
+
+				g.DrawLine(_penLens, new PointF(tr.X, tl.Y - halfWidth), new PointF(tr.X + CornerSize, tl.Y - halfWidth));
+				g.DrawLine(_penLens, new PointF(tr.X + halfWidth, tl.Y), new PointF(tr.X + halfWidth, tl.Y - CornerSize));
+
+				g.DrawLine(_penLens, new PointF(bl.X, bl.Y + halfWidth), new PointF(bl.X - CornerSize, bl.Y + halfWidth));
+				g.DrawLine(_penLens, new PointF(bl.X - halfWidth, bl.Y), new PointF(bl.X - halfWidth, bl.Y + CornerSize));
+
+				g.DrawLine(_penLens, new PointF(br.X, br.Y + halfWidth), new PointF(br.X + CornerSize, br.Y + halfWidth));
+				g.DrawLine(_penLens, new PointF(br.X + halfWidth, br.Y), new PointF(br.X + halfWidth, br.Y + CornerSize));
+			}
+		}
+
+		public override bool CanRotate { get { return false; } }
+		public override bool CanSkew { get { return false; } }
+		public override bool CanPivot { get { return false; } }
+		public override bool CanTranslate { get { return !BlockOutsideView; } }
+		public override bool CanScale { get { return !BlockOutsideView; } }
+
+		public override void Scale(Point screenPoint, Matrix sceneTransform, HoverContext context)
+		{
+			float time = GetRelativeTime();
+			bool horizontal = (context & HoverContext.ScaleHorizontal) != 0;
+			bool vertical = (context & HoverContext.ScaleVertical) != 0;
+
+			//scale is determined by first converting point to local space
+			PointF localPt = ToLocalPt(sceneTransform, screenPoint)[0];
+			PointF pivotPt = new PointF(0.5f * Width, 0.5f * Height);
+
+			float scaleX = Zoom;
+			float scaleY = Zoom;
+
+			if (context.HasFlag(HoverContext.ScaleRight))
+			{
+				float scaledDist = Width - pivotPt.X;
+				float distFromPivot = localPt.X - pivotPt.X;
+				float unscaledDist = scaledDist * Zoom;
+				scaleX = unscaledDist / distFromPivot;
+			}
+			else if (context.HasFlag(HoverContext.ScaleLeft))
+			{
+				float scaledDist = pivotPt.X;
+				float distFromPivot = pivotPt.X - localPt.X;
+				float unscaledDist = scaledDist * Zoom;
+				scaleX = unscaledDist / distFromPivot;
+			}
+			if (context.HasFlag(HoverContext.ScaleBottom))
+			{
+				float scaledDist = Height - pivotPt.Y;
+				float distFromPivot = localPt.Y - pivotPt.Y;
+				float unscaledDist = scaledDist * Zoom;
+				scaleY = unscaledDist / distFromPivot;
+			}
+			else if (context.HasFlag(HoverContext.ScaleTop))
+			{
+				float scaledDist = pivotPt.Y;
+				float distFromPivot = pivotPt.Y - localPt.Y;
+				float unscaledDist = scaledDist * Zoom;
+				scaleY = unscaledDist / distFromPivot;
+			}
+			if (horizontal && !float.IsInfinity(scaleX))
+			{
+				scaleX = (float)Math.Round(scaleX, 2);
+				if (scaleX == 0)
+				{
+					scaleX = 0.01f;
+				}
+				if (scaleX != Zoom)
+				{
+					AddValue<float>(time, "Zoom", scaleX.ToString(CultureInfo.InvariantCulture));
+				}
+			}
+			if (vertical && !float.IsInfinity(scaleY))
+			{
+				scaleY = (float)Math.Round(scaleY, 2);
+				if (scaleY == 0)
+				{
+					scaleY = 0.01f;
+				}
+				if (scaleY != Zoom)
+				{
+					AddValue<float>(time, "Zoom", scaleY.ToString(CultureInfo.InvariantCulture));
+				}
+			}
+		}
+
+		public override bool FilterRecord(string key)
+		{
+			switch (key)
+			{
+				case "pivotx":
+				case "pivoty":
+				case "z":
+					return false;
+				default:
+					return true;
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveCameraKeyframe.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveCameraKeyframe.cs
new file mode 100644
index 0000000000000000000000000000000000000000..453e58fa2bb24282457e8b6d038c72da353dca06
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveCameraKeyframe.cs	
@@ -0,0 +1,36 @@
+using System.Drawing;
+using Desktop.CommonControls.PropertyControls;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class LiveCameraKeyframe : LiveKeyframe
+	{
+		[Float(DisplayName = "Zoom", GroupOrder = 100, Description = "Zoom scaling factor for the camera", DecimalPlaces = 2, Minimum = 0.01f, Maximum = 100, Increment = 0.1f)]
+		public float? Zoom
+		{
+			get { return Get<float?>(); }
+			set { Set(value); }
+		}
+
+		[Slider(DisplayName = "Opacity (0-100)", GroupOrder = 90, Key = "alpha", Description = "Opacity/transparency level")]
+		public float? Alpha
+		{
+			get { return Get<float?>(); }
+			set { Set(value); }
+		}
+
+		[Color(DisplayName = "Color", Key = "color", GroupOrder = 85, Description = "Color")]
+		public Color Color
+		{
+			get { return Get<Color>(); }
+			set { Set(value); }
+		}
+
+		public LiveCameraKeyframe() : base()
+		{
+			TrackedProperties.Add("Zoom");
+			TrackedProperties.Add("Color");
+			TrackedProperties.Add("Alpha");
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveData.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveData.cs
new file mode 100644
index 0000000000000000000000000000000000000000..58d8759c27fc8a3acdacd507bc0552fb7b643973
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveData.cs	
@@ -0,0 +1,39 @@
+using Desktop.DataStructures;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public abstract class LiveData : BindableObject, ITimelineData
+	{
+		public abstract event EventHandler<WidgetCreationArgs> WidgetMoved;
+		public abstract event EventHandler<WidgetCreationArgs> WidgetCreated;
+		public abstract event EventHandler<WidgetCreationArgs> WidgetRemoved;
+
+		public abstract LiveObject Find(string id);
+		public abstract LiveObject GetObjectAtPoint(int x, int y, Matrix sceneTransform, bool ignoreMarkers, List<string> markers);
+		public abstract void UpdateTime(float time, float elapsedTime, bool inPlayback);
+		public abstract void UpdateRealTime(float deltaTime, bool inPlayback);
+		public abstract void Draw(Graphics g, Matrix sceneTransform, List<string> markers, LiveObject selectedObject, LiveObject selectedPreview, bool inPlayback);
+		public abstract void FitScene(int windowWidth, int windowHeight, ref Point offset, ref float zoom);
+		public abstract Matrix GetSceneTransform(int width, int height, Point offset, float zoom);
+		public abstract int BaseHeight { get; set; }
+		public abstract List<LiveObject> GetAvailableParents(LiveObject child);
+
+		public abstract bool Paste(WidgetOperationArgs args, LiveObject after);
+
+		//ITimelineData
+		public abstract List<ITimelineWidget> CreateWidgets(Timeline timeline);
+		public abstract ITimelineWidget CreateWidget(Timeline timeline, float time, object context);
+		public abstract ITimelineWidget CreateWidget(Timeline timeline, float time, object data, int index);
+		public abstract void InsertWidget(ITimelineObject widget, float time, int index);
+		public abstract int RemoveWidget(ITimelineObject widget);
+		public abstract void MoveWidget(ITimelineObject widget, int newTrack);
+		public abstract bool OnPaste(WidgetOperationArgs args);
+		public abstract void UpdateSelection(WidgetSelectionArgs args);
+		public abstract List<ITimelineBreak> CreateBreaks(Timeline timeline);
+		public abstract ITimelineBreak AddBreak(float time);
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveEmitter.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveEmitter.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ba902fd4f396d4aee31d69c5848b9031e41fed04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveEmitter.cs	
@@ -0,0 +1,513 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Globalization;
+using Desktop.CommonControls.PropertyControls;
+using SPNATI_Character_Editor.Controls.EditControls;
+using SPNATI_Character_Editor.EpilogueEditing;
+using SPNATI_Character_Editor.Properties;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class LiveEmitter : LiveAnimatedObject, ICanInvalidate
+	{
+		public EmitterWidget Widget;
+		internal Random Random = new Random();
+
+		private List<LiveParticle> _particles = new List<LiveParticle>();
+
+		private float _emissionTimer;
+
+		public event EventHandler Invalidated;
+
+		public float Rate
+		{
+			get { return Get<float>(); }
+			set { Set(value); }
+		}
+
+		[Numeric(DisplayName = "Width", Key = "width", GroupOrder = 15, Description = "Particle width")]
+		public int ParticleWidth
+		{
+			get { return Get<int>(); }
+			set { Set(value); }
+		}
+
+		[Numeric(DisplayName = "Height", Key = "height", GroupOrder = 16, Description = "Particle height")]
+		public int ParticleHeight
+		{
+			get { return Get<int>(); }
+			set { Set(value); }
+		}
+
+		[ComboBox(DisplayName = "Easing Function", Key = "ease", GroupOrder = 17, Description = "Easing function for how fast the animation progresses over time", Options = new string[] { "linear", "smooth", "ease-in", "ease-in-sin", "ease-in-cubic", "ease-out", "ease-out-sin", "ease-out-cubic", "ease-in-out-cubic", "ease-out-in", "ease-out-in-cubic", "bounce", "elastic" })]
+		public string ParticleEase
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[Boolean(DisplayName = "Ignore rotation", Key = "ignoreRotation", GroupOrder = 19, Description = "If true, particles will spawn facing upwards regardless of the emitter's current rotation")]
+		public bool IgnoreRotation
+		{
+			get { return Get<bool>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "Particle Life (s)", Key = "lifetime", GroupOrder = 20, Description = "Time in seconds before an emitted object disappears", Minimum = 0.1f, Maximum = 1000, DecimalPlaces = 2)]
+		public RandomParameter Lifetime
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[Float(DisplayName = "Angle", Key = "angle", GroupOrder = 21, Description = "Degrees away from emitter's forward direction that objects can be emitted in", Minimum = -180, Maximum = 180, DecimalPlaces = 0)]
+		public float Angle
+		{
+			get { return Get<float>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "Start Scale X", Key = "startScaleX", GroupOrder = 22, Description = "Initial horizontal stretching range", Minimum = -1000, Maximum = 1000, DecimalPlaces = 2)]
+		public RandomParameter StartScaleX
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "Start Scale Y", Key = "startScaleY", GroupOrder = 23, Description = "Initial vertical stretching range", Minimum = -1000, Maximum = 1000, DecimalPlaces = 2)]
+		public RandomParameter StartScaleY
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "End Scale X", Key = "endScaleX", GroupOrder = 24, Description = "Ending horizontal stretching range", Minimum = -1000, Maximum = 1000, DecimalPlaces = 2)]
+		public RandomParameter EndScaleX
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "End Scale Y", Key = "endScaleY", GroupOrder = 25, Description = "Ending vertical stretching range", Minimum = -1000, Maximum = 1000, DecimalPlaces = 2)]
+		public RandomParameter EndScaleY
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "Speed", Key = "speed", GroupOrder = 26, Description = "Initial speed range (px/sec)", Minimum = -1000, Maximum = 1000, DecimalPlaces = 0)]
+		public RandomParameter Speed
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "Acceleration", Key = "accel", GroupOrder = 27, Description = "Initial acceleration range (px/sec^2)", Minimum = -1000, Maximum = 1000, DecimalPlaces = 0)]
+		public RandomParameter Acceleration
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "Force X", Key = "forceX", GroupOrder = 28, Description = "World horizontal force (wind) (px/sec^2)", Minimum = -1000, Maximum = 1000, DecimalPlaces = 0)]
+		public RandomParameter ForceX
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "Force Y", Key = "forceY", GroupOrder = 29, Description = "World vertical force (gravity) (px/sec^2)", Minimum = -1000, Maximum = 1000, DecimalPlaces = 0)]
+		public RandomParameter ForceY
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleColor(DisplayName = "Start Color", Key = "startColor", GroupOrder = 30, Description = "Initial particle color")]
+		public RandomColor StartColor
+		{
+			get { return Get<RandomColor>(); }
+			set { Set(value); }
+		}
+
+		[ParticleColor(DisplayName = "End Color", Key = "startColor", GroupOrder = 31, Description = "Ending particle color")]
+		public RandomColor EndColor
+		{
+			get { return Get<RandomColor>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "Start Alpha", Key = "startAlpha", GroupOrder = 32, Description = "Initial transparency range", Minimum = 0, Maximum = 100, DecimalPlaces = 0)]
+		public RandomParameter StartAlpha
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "End Alpha", Key = "endAlpha", GroupOrder = 33, Description = "Ending transparency range", Minimum = 0, Maximum = 100, DecimalPlaces = 0)]
+		public RandomParameter EndAlpha
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "Start Spin", Key = "startRotation", GroupOrder = 34, Description = "Initial spin range", Minimum = -1000, Maximum = 1000, DecimalPlaces = 0)]
+		public RandomParameter StartRotation
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "End Spin", Key = "endRotation", GroupOrder = 35, Description = "Ending spin range", Minimum = -1000, Maximum = 1000, DecimalPlaces = 0)]
+		public RandomParameter EndRotation
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "Start Skew X", Key = "startSkewX", GroupOrder = 36, Description = "Initial horizontal shearing range", Minimum = -89, Maximum = 89, DecimalPlaces = 2)]
+		public RandomParameter StartSkewX
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "Start Skew Y", Key = "startSkewY", GroupOrder = 36, Description = "Initial vertical shearing range", Minimum = -89, Maximum = 89, DecimalPlaces = 2)]
+		public RandomParameter StartSkewY
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "End Skew X", Key = "endSkewX", GroupOrder = 37, Description = "Ending horizontal shearing range", Minimum = -89, Maximum = 89, DecimalPlaces = 2)]
+		public RandomParameter EndSkewX
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		[ParticleFloat(DisplayName = "End Skew Y", Key = "endSkewY", GroupOrder = 37, Description = "Ending vertical shearing range", Minimum = -89, Maximum = 89, DecimalPlaces = 2)]
+		public RandomParameter EndSkewY
+		{
+			get { return Get<RandomParameter>(); }
+			set { Set(value); }
+		}
+
+		public LiveEmitter()
+		{
+			DisplayPastEnd = false;
+			CenterX = true;
+			CenterY = true;
+			PivotX = 0.5f;
+			PivotY = 0.5f;
+			ScaleX = 0.5f;
+			ScaleY = 0.5f;
+			ParticleWidth = 10;
+			ParticleHeight = 10;
+			Lifetime = new RandomParameter(1, 1);
+			StartAlpha = new RandomParameter(100, 100);
+			EndAlpha = new RandomParameter(0, 0);
+		}
+
+		public LiveEmitter(LiveData data, float time) : this()
+		{
+			Data = data;
+			DisplayPastEnd = false;
+			Length = 1;
+			Start = time;
+
+			LiveEmitterKeyframe startFrame = CreateKeyframe(0) as LiveEmitterKeyframe;
+			AddKeyframe(startFrame);
+			Update(time, 0, false);
+			UpdateLocalTransform();
+		}
+
+		#region Epilogue import
+		public LiveEmitter(LiveScene scene, Directive directive, Character character, float time) : this()
+		{
+			DisplayPastEnd = false;
+			Data = scene;
+			ParentId = directive.ParentId;
+			Length = 0.5f;
+			Id = directive.Id;
+			Z = directive.Layer;
+			Start = time;
+			if (!string.IsNullOrEmpty(directive.Delay))
+			{
+				float start;
+				float.TryParse(directive.Delay, NumberStyles.Number, CultureInfo.InvariantCulture, out start);
+				Start += start;
+				Length = 1;
+			}
+
+			if (!string.IsNullOrEmpty(directive.Angle))
+			{
+				float angle;
+				float.TryParse(directive.Delay, NumberStyles.Number, CultureInfo.InvariantCulture, out angle);
+				Angle = angle;
+			}
+
+			InitializeParameters(directive);
+
+			if (!string.IsNullOrEmpty(directive.Width))
+			{
+			}
+			if (!string.IsNullOrEmpty(directive.Height))
+			{
+			}
+
+			LiveKeyframe temp;
+			string oldSrc = directive.Src;
+			if (!string.IsNullOrEmpty(directive.Src))
+			{
+				directive.Src = character.FolderName + "/" + directive.Src;
+			}
+			AddKeyframe(directive, 0, false, out temp);
+			directive.Src = oldSrc;
+			Update(time, 0, false);
+		}
+
+		private void InitializeParameters(Directive directive)
+		{
+			if (!string.IsNullOrEmpty(directive.StartScaleX))
+			{
+				StartScaleX = RandomParameter.Create(directive.StartScaleX, 1, 1);
+			}
+			if (!string.IsNullOrEmpty(directive.EndScaleX))
+			{
+				EndScaleX = RandomParameter.Create(directive.EndScaleX, StartScaleX);
+			}
+			if (!string.IsNullOrEmpty(directive.StartScaleY))
+			{
+				StartScaleY = RandomParameter.Create(directive.StartScaleY, 1, 1);
+			}
+			if (!string.IsNullOrEmpty(directive.EndScaleY))
+			{
+				EndScaleY = RandomParameter.Create(directive.EndScaleY, StartScaleY);
+			}
+			if (!string.IsNullOrEmpty(directive.StartSkewX))
+			{
+				StartSkewX = RandomParameter.Create(directive.StartSkewX, 0, 0);
+			}
+			if (!string.IsNullOrEmpty(directive.EndSkewX))
+			{
+				EndSkewX = RandomParameter.Create(directive.EndSkewX, StartSkewX);
+			}
+			if (!string.IsNullOrEmpty(directive.StartSkewY))
+			{
+				StartSkewY = RandomParameter.Create(directive.StartSkewY, 0, 0);
+			}
+			if (!string.IsNullOrEmpty(directive.EndSkewY))
+			{
+				EndSkewY = RandomParameter.Create(directive.EndSkewY, StartSkewY);
+			}
+			if (!string.IsNullOrEmpty(directive.Speed))
+			{
+				Speed = RandomParameter.Create(directive.Speed, 0, 0);
+			}
+			if (!string.IsNullOrEmpty(directive.Acceleration))
+			{
+				Acceleration = RandomParameter.Create(directive.Acceleration, 0, 0);
+			}
+			if (!string.IsNullOrEmpty(directive.ForceX))
+			{
+				ForceX = RandomParameter.Create(directive.ForceX, 0, 0);
+			}
+			if (!string.IsNullOrEmpty(directive.ForceY))
+			{
+				ForceY = RandomParameter.Create(directive.ForceY, 0, 0);
+			}
+			if (!string.IsNullOrEmpty(directive.StartColor))
+			{
+				StartColor = RandomColor.Create(directive.StartColor, Color.White, Color.White);
+			}
+			if (!string.IsNullOrEmpty(directive.EndColor))
+			{
+				EndColor = RandomColor.Create(directive.EndColor, StartColor);
+			}
+			if (!string.IsNullOrEmpty(directive.StartAlpha))
+			{
+				StartAlpha = RandomParameter.Create(directive.StartAlpha, 100, 100);
+			}
+			if (!string.IsNullOrEmpty(directive.EndAlpha))
+			{
+				EndAlpha = RandomParameter.Create(directive.EndAlpha, StartAlpha);
+			}
+			if (!string.IsNullOrEmpty(directive.StartRotation))
+			{
+				StartRotation = RandomParameter.Create(directive.StartRotation, 0, 0);
+			}
+			if (!string.IsNullOrEmpty(directive.EndRotation))
+			{
+				EndRotation = RandomParameter.Create(directive.EndRotation, StartRotation);
+			}
+			if (!string.IsNullOrEmpty(directive.Lifetime))
+			{
+				Lifetime = RandomParameter.Create(directive.Lifetime, 1, 1);
+			}
+			IgnoreRotation = directive.IgnoreRotation;
+		}
+		#endregion
+
+		public override string GetLabel()
+		{
+			return $"Emitter Settings: {Id}";
+		}
+
+		public override Type GetKeyframeType()
+		{
+			return typeof(LiveEmitterKeyframe);
+		}
+
+		protected override void ParseKeyframe(Keyframe kf, bool addBreak, HashSet<string> properties, float time)
+		{
+			if (!string.IsNullOrEmpty(kf.X))
+			{
+				AddValue<float>(time, "X", kf.X, addBreak);
+				properties.Add("X");
+			}
+			if (!string.IsNullOrEmpty(kf.Y))
+			{
+				AddValue<float>(time, "Y", kf.Y, addBreak);
+				properties.Add("Y");
+			}
+			if (!string.IsNullOrEmpty(kf.Src))
+			{
+				AddValue<string>(time, "Src", kf.Src, addBreak);
+				properties.Add("Src");
+			}
+			if (!string.IsNullOrEmpty(kf.Rotation))
+			{
+				AddValue<float>(time, "Rotation", kf.Rotation, addBreak);
+				properties.Add("Rotation");
+			}
+			if (!string.IsNullOrEmpty(kf.Rate))
+			{
+				AddValue<float>(time, "Rate", kf.Rate, addBreak);
+				properties.Add("Rate");
+			}
+		}
+
+		public override void UpdateRealTime(float elapsed, bool inPlayback)
+		{
+			if (inPlayback || LinkedPreview != null)
+			{
+				_emissionTimer += elapsed;
+				float cooldown = 1000 / Rate;
+				while (_emissionTimer >= cooldown)
+				{
+					Emit();
+					_emissionTimer -= cooldown;
+				}
+			}
+			else
+			{
+				_emissionTimer = 0;
+			}
+
+			if (_particles.Count > 0)
+			{
+				for (int i = 0; i < _particles.Count; i++)
+				{
+					_particles[i].UpdateRealTime(elapsed, inPlayback);
+					if (_particles[i].IsDead)
+					{
+						_particles.RemoveAt(i--);
+					}
+				}
+				Invalidated?.Invoke(this, EventArgs.Empty);
+			}
+		}
+
+		protected override void OnUpdate(float time, float offset, string easeOverride, string interpolationOverride, bool? looped, bool inPlayback)
+		{
+			X = GetPropertyValue("X", time, offset, 0.0f, easeOverride, interpolationOverride, looped);
+			Y = GetPropertyValue("Y", time, offset, 0.0f, easeOverride, interpolationOverride, looped);
+			Rate = GetPropertyValue("Rate", time, offset, 0.0f, easeOverride, interpolationOverride, looped);
+			string src = GetPropertyValue<string>("Src", time, offset, null, easeOverride, interpolationOverride, looped);
+			Src = src;
+			Image = LiveImageCache.Get(src);
+			Rotation = GetPropertyValue("Rotation", time, offset, 0.0f, easeOverride, interpolationOverride, looped);
+		}
+
+		public override void DestroyLivePreview()
+		{
+			base.DestroyLivePreview();
+			_particles.Clear();
+		}
+
+		private void Emit()
+		{
+			_particles.Add(new LiveParticle(this));
+		}
+
+		public override ITimelineWidget CreateWidget(Timeline timeline)
+		{
+			return new EmitterWidget(this, timeline);
+		}
+
+		protected override void OnUpdateDimensions()
+		{
+			Width = Resources.emitter.Width;
+			Height = Resources.emitter.Height;
+		}
+
+		public override bool FilterRecord(string key)
+		{
+			switch (key)
+			{
+				case "x":
+				case "y":
+				case "pivotx":
+				case "pivoty":
+					return false;
+				default:
+					return true;
+			}
+		}
+
+		public override void Draw(Graphics g, Matrix sceneTransform, List<string> markers, bool inPlayback)
+		{
+			if (HiddenByMarker(markers))
+			{
+				return;
+			}
+
+			//emitter
+			if (IsVisible && !Hidden)
+			{
+				g.MultiplyTransform(WorldTransform);
+				g.MultiplyTransform(sceneTransform, MatrixOrder.Append);
+
+				Image img = Resources.emitter;
+
+				//angle
+				if (LinkedPreview != null)
+				{
+					int angleLength = img.Width;
+					int dx = Angle == 90 ? angleLength : (int)(Math.Sin(Angle * Math.PI / 180.0f) * angleLength);
+					int dy = (int)(Math.Cos(Angle * Math.PI / 180.0f) * angleLength);
+
+					g.DrawLine(Pens.LightGray, Width / 2, Height / 2, Width / 2 + dx, Height / 2 - dy);
+					g.DrawLine(Pens.LightGray, Width / 2, Height / 2, Width / 2 - dx, Height / 2 - dy);
+				}
+
+				g.DrawImage(img, new Rectangle(0, 0, Width, Height), new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);
+
+				g.ResetTransform();
+			}
+
+			//particles
+			foreach (LiveParticle particle in _particles)
+			{
+				particle.Draw(g, sceneTransform, markers, inPlayback);
+			}
+		}
+
+		public override bool CanScale { get { return false; } }
+		public override bool CanSkew { get { return false; } }
+		public override bool CanPivot { get { return false; } }
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveEmitterKeyframe.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveEmitterKeyframe.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0bcaaf25c699235f74813da44129a406dbcf7b2a
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveEmitterKeyframe.cs	
@@ -0,0 +1,45 @@
+using Desktop.CommonControls.PropertyControls;
+using SPNATI_Character_Editor.Controls;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class LiveEmitterKeyframe : LiveKeyframe
+	{
+		public LiveEmitterKeyframe() : base()
+		{
+			TrackedProperties.Add("Src");
+			TrackedProperties.Add("Rotation");
+			TrackedProperties.Add("Rate");
+			TrackedProperties.Add("Burst");
+			Rate = 1;
+		}
+
+		[FileSelect(DisplayName = "Source", GroupOrder = 10, Key = "src", Description = "Particle source image")]
+		public string Src
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[Float(DisplayName = "Rotation (deg)", GroupOrder = 50, Key = "rotation", Description = "Emitter rotation", DecimalPlaces = 0, Minimum = -7020, Maximum = 7020)]
+		public float? Rotation
+		{
+			get { return Get<float?>(); }
+			set { Set(value); }
+		}
+
+		[Float(DisplayName = "Rate", Key = "rate", GroupOrder = 25, Description = "Emissions per second", Minimum = 0, Maximum = 100, DecimalPlaces = 2)]
+		public float? Rate
+		{
+			get { return Get<float?>(); }
+			set { Set(value); }
+		}
+
+		[Numeric(DisplayName = "Burst", Key = "burst", GroupOrder = 100, Description = "Number of particles to emit at this moment", Minimum = 1, Maximum = 100)]
+		public int? Burst
+		{
+			get { return Get<int?>(); }
+			set { Set(value); }
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveKeyframe.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveKeyframe.cs
index 9e3459e3d54a211b0281287be86f954d8e9f9d3e..942f31b030cd6b260939598a697b7e4e44ed3636 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveKeyframe.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveKeyframe.cs	
@@ -1,9 +1,8 @@
 using System;
 using System.Collections.Generic;
+using Desktop;
 using Desktop.CommonControls.PropertyControls;
 using Desktop.DataStructures;
-using SPNATI_Character_Editor.Controls;
-using Desktop;
 
 namespace SPNATI_Character_Editor.EpilogueEditor
 {
@@ -12,11 +11,9 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 	/// </summary>
 	public class LiveKeyframe : BindableObject, ILabel
 	{
-		public static List<string> TrackedProperties;
-
 		public event EventHandler LabelChanged;
 
-		public LiveSprite Sprite;
+		public LiveAnimatedObject Data;
 
 		[Float(DisplayName = "Time", Key = "time", GroupOrder = 0, Increment = 0.1f)]
 		public float Time
@@ -29,12 +26,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			}
 		}
 
-		[FileSelect(DisplayName = "Source", GroupOrder = 10, Key = "src", Description = "Sprite source image")]
-		public string Src
-		{
-			get { return Get<string>(); }
-			set { Set(value); }
-		}
+		public HashSet<string> TrackedProperties { get; }
 
 		[Float(DisplayName = "X", Key = "x", GroupOrder = 15, Minimum = -100000, Maximum = 100000, DecimalPlaces = 0)]
 		public float? X
@@ -50,71 +42,38 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			set { Set(value); }
 		}
 
-		[Float(DisplayName = "Scale X", GroupOrder = 40, Key = "scalex", Increment = 0.1f, Minimum = -1000, Maximum = 1000)]
-		public float? ScaleX
-		{
-			get { return Get<float?>(); }
-			set { Set(value); }
-		}
-
-		[Float(DisplayName = "Scale Y", GroupOrder = 45, Key = "scaley", Increment = 0.1f, Minimum = -1000, Maximum = 1000)]
-		public float? ScaleY
-		{
-			get { return Get<float?>(); }
-			set { Set(value); }
-		}
-
-		[Slider(DisplayName = "Opacity (0-100)", GroupOrder = 30, Key = "alpha", Description = "Opacity/transparency level")]
-		public float? Alpha
-		{
-			get { return Get<float?>(); }
-			set { Set(value); }
-		}
-
-		[Float(DisplayName = "Rotation (deg)", GroupOrder = 50, Key = "rotation", Description = "Sprite rotation", DecimalPlaces = 0, Minimum = -7020, Maximum = 7020)]
-		public float? Rotation
-		{
-			get { return Get<float?>(); }
-			set { Set(value); }
-		}
-
-		[Float(DisplayName = "Skew X", GroupOrder = 60, Key = "skewx", Description = "Sprite shearing factor horizontally", DecimalPlaces = 2, Minimum = -89, Maximum = 89, Increment = 1f)]
-		public float? SkewX
-		{
-			get { return Get<float?>(); }
-			set { Set(value); }
-		}
-
-		[Float(DisplayName = "Skew Y", GroupOrder = 65, Key = "skewx", Description = "Sprite shearing factor vertically", DecimalPlaces = 2, Minimum = -89, Maximum = 89, Increment = 1f)]
-		public float? SkewY
-		{
-			get { return Get<float?>(); }
-			set { Set(value); }
-		}
-
 		/// <summary>
-		/// Properties that should not be interpolated from the previous frame, like being the start of a completely separate animation
+		/// Data specific to a single property within the keyframe
 		/// </summary>
-		public ObservableDictionary<string, bool> InterpolationBreaks
+		public ObservableDictionary<string, LiveKeyframeMetadata> PropertyMetadata
 		{
-			get { return Get<ObservableDictionary<string, bool>>(); }
+			get { return Get<ObservableDictionary<string, LiveKeyframeMetadata>>(); }
 			set { Set(value); }
 		}
 
 		public LiveKeyframe()
 		{
-			InterpolationBreaks = new ObservableDictionary<string, bool>();
+			PropertyMetadata = new ObservableDictionary<string, LiveKeyframeMetadata>();
+			TrackedProperties = new HashSet<string>();
+			TrackedProperties.Add("X");
+			TrackedProperties.Add("Y");
 		}
 
-		public LiveKeyframe(float time) : this()
+		public virtual bool FilterRecord(PropertyRecord record)
 		{
-			Time = time;
+			if (record.Key == "time" && Time == 0)
+			{
+				return false;
+			}
+			return true;
 		}
 
-		static LiveKeyframe()
+		public KeyframeType GetFrameType(string property)
 		{
-			TrackedProperties = new List<string>();
-			TrackedProperties.AddRange(new string[] { "Src", "X", "Y", "ScaleX", "ScaleY", "Alpha", "Rotation", "SkewX", "SkewY" });
+			if (string.IsNullOrEmpty(property)) { return KeyframeType.Normal; }
+			LiveKeyframeMetadata metadata = GetMetadata(property, false);
+			if (metadata == null) { return KeyframeType.Normal; }
+			return metadata.FrameType;
 		}
 
 		public override string ToString()
@@ -124,7 +83,8 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		public string GetLabel()
 		{
-			return $"Keyframe: {Sprite.Id} ({Time}s)";
+			if (Data == null) { return "???"; }
+			return $"Keyframe: {Data.Id} ({Time}s)";
 		}
 
 		/// <summary>
@@ -134,9 +94,9 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		{
 			get
 			{
-				for (int i = 0; i < TrackedProperties.Count; i++)
+				foreach (string propName in TrackedProperties)
 				{
-					if (HasProperty(TrackedProperties[i]))
+					if (HasProperty(propName))
 					{
 						return false;
 					}
@@ -153,9 +113,9 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			get
 			{
 				int count = 0;
-				for (int i = 0; i < TrackedProperties.Count; i++)
+				foreach (string propName in TrackedProperties)
 				{
-					if (HasProperty(TrackedProperties[i]))
+					if (HasProperty(propName))
 					{
 						count++;
 					}
@@ -163,5 +123,23 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				return count;
 			}
 		}
+
+		public LiveKeyframeMetadata GetMetadata(string property, bool addIfNew)
+		{
+			if (!PropertyMetadata.ContainsKey(property))
+			{
+				if (addIfNew)
+				{
+					LiveKeyframeMetadata metadata = new LiveKeyframeMetadata(property);
+					PropertyMetadata.Add(property, metadata);
+					return metadata;
+				}
+				else
+				{
+					return new LiveKeyframeMetadata(property);
+				}
+			}
+			return PropertyMetadata[property];
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveKeyframeMetadata.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveKeyframeMetadata.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9ae6195a022787a4251fb74da4aa558b790c824e
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveKeyframeMetadata.cs	
@@ -0,0 +1,96 @@
+using Desktop.CommonControls.PropertyControls;
+using Desktop.DataStructures;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class LiveKeyframeMetadata : BindableObject
+	{
+		private string _property;
+
+		/// <summary>
+		/// Type of keyframe (start of new set, split, mid-animation, etc.)
+		/// </summary>
+		public KeyframeType FrameType
+		{
+			get { return Get<KeyframeType>(); }
+			set { Set(value); }
+		}
+
+		public string Ease
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		public string Interpolation
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		/// <summary>
+		/// Whether this frame is the start of a loop
+		/// </summary>
+		public bool Looped
+		{
+			get { return Get<bool>(); }
+			set { Set(value); }
+		}
+
+		[ComboBox(DisplayName = "Repeat Method", Key = "clamp", GroupOrder = 43, Description = "How a looping animation loops", Options = new string[] { "clamp", "wrap", "mirror" })]
+		/// <summary>
+		/// Clamping method to use for restricting a looped animations's time between 0 and 1
+		/// </summary>
+		public string ClampMethod
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[Numeric(DisplayName = "Iterations", Key = "iterations", GroupOrder = 44, Description = "How many times to repeat a looped animation. 0 means infinite.", Minimum = 0, Maximum = 1000)]
+		/// <summary>
+		/// Number of iterations to loop, if Looped is true. 0 means loop indefinitely.
+		/// </summary>
+		public int Iterations
+		{
+			get { return Get<int>(); }
+			set { Set(value); }
+		}
+
+		public LiveKeyframeMetadata() { }
+
+		public LiveKeyframeMetadata(string property)
+		{
+			_property = property;
+			Interpolation = (property == "Src" || property == "Text" || property == "Burst") ? "none" : "linear";
+			Ease = (property == "Src" || property == "Text") ? "linear" : "smooth";
+		}
+
+		public string ToKey()
+		{
+			string looped = Looped ? "1" : "";
+			return $"{looped}|{(Ease ?? "")}|{(Interpolation ?? "")}|{(ClampMethod ?? "")}|{Iterations}";
+		}
+
+		public override string ToString()
+		{
+			return $"{_property} - Keyframe Animation Settings";
+		}
+	}
+
+	public enum KeyframeType
+	{
+		/// <summary>
+		/// Normal mid-animation keyframe
+		/// </summary>
+		Normal,
+		/// <summary>
+		/// Beginning of a new animation
+		/// </summary>
+		Begin,
+		/// <summary>
+		/// End of previous and beginning of next
+		/// </summary>
+		Split,
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveObject.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveObject.cs
new file mode 100644
index 0000000000000000000000000000000000000000..18540f3d62c79a0cde3eaf537d65a21ef1e90641
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveObject.cs	
@@ -0,0 +1,823 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Globalization;
+using Desktop.CommonControls.PropertyControls;
+using Desktop.DataStructures;
+using SPNATI_Character_Editor.Controls;
+using SPNATI_Character_Editor.Controls.EditControls;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	/// <summary>
+	/// Base class for working objects in an epilogue or pose
+	/// </summary>
+	public abstract class LiveObject : BindableObject, ILabel, IComparable<LiveObject>
+	{
+		public event EventHandler PreviewInvalidated;
+		public event EventHandler LabelChanged;
+		public LiveData Data;
+		public bool CenterX { get; set; } = true;
+		public bool CenterY { get; set; } = false;
+
+		private static Pen _penOuterSelection;
+		private static Pen _penInnerSelection;
+		private static Brush _brushHandle;
+		private static Pen _penPivotBox;
+
+		static LiveObject()
+		{
+			_penPivotBox = new Pen(Color.FromArgb(127, 255, 255, 255));
+			_penPivotBox.Width = 2;
+
+			_brushHandle = new SolidBrush(Color.Black);
+			_penOuterSelection = new Pen(Brushes.White, 3);
+			_penOuterSelection.DashStyle = DashStyle.DashDotDot;
+			_penInnerSelection = new Pen(Brushes.Black, 1);
+			_penInnerSelection.DashStyle = DashStyle.DashDotDot;
+		}
+
+		/// <summary>
+		/// Set on a source object pointing to its live preview
+		/// </summary>
+		public LiveObject LinkedPreview;
+
+		[Text(DisplayName = "Id", Key = "id", GroupOrder = 0)]
+		public string Id
+		{
+			get { return Get<string>(); }
+			set
+			{
+				Set(value);
+				LabelChanged?.Invoke(this, EventArgs.Empty);
+			}
+		}
+
+		public LiveObject Parent { get; private set; }
+		public string ParentId
+		{
+			get { return Get<string>(); }
+			set
+			{
+				Set(value);
+				Parent = Data.Find(value);
+				UpdateLocalTransform();
+			}
+		}
+
+		/// <summary>
+		/// Hide from canvas UI
+		/// </summary>
+		public bool Hidden
+		{
+			get { return Get<bool>(); }
+			set { Set(value); }
+		}
+
+		public abstract bool IsVisible { get; }
+
+		//Not exposing since there are still some questions about the best way to recognize this from a PoseDirective
+		//[Boolean(DisplayName = "Full Length", GroupOrder = 10, Description = "If checked, the sprite has no set end time.")]
+		public bool LinkedToEnd
+		{
+			get { return Get<bool>(); }
+			set { Set(value); }
+		}
+
+		[DirectiveMarker(DisplayName = "Marker", GroupOrder = 13, Key = "marker", Description = "Run this directive only if the marker's condition is met", ShowPrivate = true)]
+		public string Marker
+		{
+			get { return Get<string>(); }
+			set
+			{
+				bool perTarget;
+				MarkerOperator op;
+				string markerValue;
+				MarkerName = SPNATI_Character_Editor.Marker.ExtractConditionPieces(value, out op, out markerValue, out perTarget);
+				MarkerOp = op;
+				MarkerValue = markerValue;
+				Set(value);
+			}
+		}
+
+		private string MarkerName;
+		private MarkerOperator MarkerOp;
+		private string MarkerValue;
+
+		[Numeric(DisplayName = "Layer", Key = "z", GroupOrder = 15, Minimum = -100)]
+		public int Z
+		{
+			get { return Get<int>(); }
+			set { Set(value); }
+		}
+
+		[Float(DisplayName = "Pivot X", Key = "pivotx", GroupOrder = 20, Description = "X value of rotation/scale point of origin as a percentage of the sprite's physical size.", Minimum = -1000, Maximum = 1000, Increment = 0.1f)]
+		public float PivotX
+		{
+			get { return Get<float>(); }
+			set
+			{
+				if (float.IsNaN(value))
+				{
+					value = 0;
+				}
+				Set(value);
+				UpdateLocalTransform();
+			}
+		}
+		[Float(DisplayName = "Pivot Y", Key = "pivoty", GroupOrder = 20, Description = "Y value of rotation/scale point of origin as a percentage of the sprite's physical size.", Minimum = -1000, Maximum = 1000, Increment = 0.1f)]
+		public float PivotY
+		{
+			get { return Get<float>(); }
+			set
+			{
+				if (float.IsNaN(value))
+				{
+					value = 0;
+				}
+				Set(value);
+				UpdateLocalTransform();
+			}
+		}
+
+		[Float(DisplayName = "Start", Key = "start", GroupOrder = 5, Description = "Starting time to display the sprite.", Minimum = 0, Maximum = 1000, Increment = 0.1f)]
+		public float Start
+		{
+			get { return Get<float>(); }
+			set { Set(value); }
+		}
+
+		public float Time { get; protected set; }
+		public float TimeOffset { get; protected set; }
+
+		protected float GetRelativeTime()
+		{
+			return Time - Start;
+		}
+
+		public int CompareTo(LiveObject other)
+		{
+			return Id.CompareTo(other.Id);
+		}
+
+		public virtual string GetLabel()
+		{
+			return Id;
+		}
+
+		public override string ToString()
+		{
+			return $"{Id} ({Start})";
+		}
+
+		public abstract ITimelineWidget CreateWidget(Timeline timeline);
+
+		#region Interpolated values based on the current time
+		public string Src;
+		public Bitmap Image;
+
+		public int Height
+		{
+			get { return Get<int>(); }
+			set { Set(value); }
+		}
+
+		public int Width
+		{
+			get { return Get<int>(); }
+			set { Set(value); }
+		}
+
+		private float _x;
+		public float X
+		{
+			get { return _x; }
+			set
+			{
+				if (_x != value)
+				{
+					_x = (float)Math.Round(value, 0); UpdateLocalTransform(); NotifyPropertyChanged(nameof(X));
+				}
+			}
+		}
+
+		private float _y;
+		public float Y
+		{
+			get { return _y; }
+			set
+			{
+				if (_y != value)
+				{
+					_y = (float)Math.Round(value, 0); UpdateLocalTransform(); NotifyPropertyChanged(nameof(Y));
+				}
+			}
+		}
+
+		private float _rotation;
+		public float Rotation
+		{
+			get { return _rotation; }
+			set { _rotation = (float)Math.Round(value, 2); UpdateLocalTransform(); }
+		}
+
+		private float _scaleX = 1;
+		public float ScaleX
+		{
+			get { return _scaleX; }
+			set
+			{
+				value = (float)Math.Round(value, 2);
+				if (value == 0)
+				{
+					value = 0.01f;
+				}
+				_scaleX = value;
+				UpdateLocalTransform();
+			}
+		}
+
+		private float _scaleY = 1;
+		public float ScaleY
+		{
+			get { return _scaleY; }
+			set
+			{
+				value = (float)Math.Round(value, 2);
+				if (value == 0)
+				{
+					value = 0.01f;
+				}
+				_scaleY = value;
+				UpdateLocalTransform();
+			}
+		}
+
+		private float _skewX = 0;
+		public float SkewX
+		{
+			get { return _skewX; }
+			set
+			{
+				value = (float)Math.Round(value, 2);
+				_skewX = value;
+			}
+		}
+
+		private float _skewY = 0;
+		public float SkewY
+		{
+			get { return _skewY; }
+			set
+			{
+				value = (float)Math.Round(value, 2);
+				_skewY = value;
+			}
+		}
+
+		public float Alpha = 100;
+
+		public Matrix LocalTransform { get; protected set; }
+
+		protected virtual void UpdateLocalTransform()
+		{
+			Matrix transform = new Matrix();
+			float pivotX = PivotX * Width;
+			float pivotY = PivotY * Height;
+			transform.Translate(-pivotX, -pivotY, MatrixOrder.Append);
+			transform.Scale(ScaleX, ScaleY, MatrixOrder.Append);
+			transform.Rotate(Rotation, MatrixOrder.Append);
+			transform.Translate(pivotX, pivotY, MatrixOrder.Append);
+
+			transform.Translate(X - (Parent == null && CenterX ? Width / 2 : 0), Y - (Parent == null && CenterY ? Height / 2 : 0), MatrixOrder.Append); //local position
+			LocalTransform = transform;
+		}
+
+		public Matrix WorldTransform
+		{
+			get
+			{
+				LiveObject transform = this;
+				Matrix m = new Matrix();
+				while (transform != null)
+				{
+					m.Multiply(transform.LocalTransform, MatrixOrder.Append);
+
+					if (transform.Parent == this)
+					{
+						transform = null;
+					}
+					else
+					{
+						transform = transform.Parent;
+					}
+				}
+				return m;
+			}
+		}
+
+		public Matrix UnscaledWorldTransform
+		{
+			get
+			{
+				LiveObject transform = this;
+				Matrix m = new Matrix();
+				while (transform != null)
+				{
+					Matrix localTransform;
+					if (transform == this)
+					{
+						localTransform = new Matrix();
+						localTransform.Translate(X - (Parent == null && CenterX ? Width / 2 : 0), Y - (Parent == null && CenterY ? Height / 2 : 0), MatrixOrder.Append);
+					}
+					else
+					{
+						localTransform = transform.LocalTransform;
+					}
+
+					m.Multiply(localTransform, MatrixOrder.Append);
+
+					if (transform.Parent == this)
+					{
+						transform = null;
+					}
+					else
+					{
+						transform = transform.Parent;
+					}
+				}
+				return m;
+			}
+		}
+
+		/// <summary>
+		/// Converts a point from local space to screen space
+		/// </summary>
+		/// <param name="x"></param>
+		/// <param name="y"></param>
+		/// <returns></returns>
+		public PointF ToScreenPt(float x, float y, Matrix sceneTransform)
+		{
+			PointF[] pt = new PointF[] { new PointF(x, y) };
+			return ToScreenPt(sceneTransform, pt)[0];
+		}
+
+		/// <summary>
+		/// Converts a point from local space to screen space
+		/// </summary>
+		/// <param name="x"></param>
+		/// <param name="y"></param>
+		/// <returns></returns>
+		public virtual PointF[] ToScreenPt(Matrix sceneTransform, params PointF[] pts)
+		{
+			Matrix m = new Matrix();
+			m.Multiply(sceneTransform);
+			m.Multiply(WorldTransform);
+			m.TransformPoints(pts);
+			return pts;
+		}
+
+		/// <summary>
+		/// Converts a point from screen spcae to local space
+		/// </summary>
+		/// <param name="x"></param>
+		/// <param name="y"></param>
+		/// <param name="sceneTransform"></param>
+		/// <returns></returns>
+		public PointF ToLocalPt(float x, float y, Matrix sceneTransform)
+		{
+			PointF[] pt = new PointF[] { new PointF(x, y) };
+			return ToLocalPt(sceneTransform, pt)[0];
+		}
+
+		/// <summary>
+		/// Converts one or more points in screen space to local space
+		/// </summary>
+		/// <param name="sceneTransform"></param>
+		/// <param name="pts"></param>
+		/// <returns></returns>
+		public virtual PointF[] ToLocalPt(Matrix sceneTransform, params PointF[] pts)
+		{
+			Matrix m = new Matrix();
+			m.Multiply(sceneTransform);
+			m.Multiply(WorldTransform);
+			m.Invert();
+			m.TransformPoints(pts);
+			return pts;
+		}
+
+		/// <summary>
+		/// Converts a point in local space to world space
+		/// </summary>
+		/// <param name="x"></param>
+		/// <param name="y"></param>
+		/// <returns></returns>
+		public PointF ToWorldPt(float x, float y)
+		{
+			PointF[] pt = new PointF[] { new PointF(x, y) };
+			return ToWorldPt(pt)[0];
+		}
+		/// <summary>
+		/// Converts one or more points in local space to world space
+		/// </summary>
+		/// <param name="sceneTransform"></param>
+		/// <param name="pts"></param>
+		/// <returns></returns>
+		public virtual PointF[] ToWorldPt(params PointF[] pts)
+		{
+			if (Parent == null)
+			{
+				return pts;
+			}
+			Matrix m = new Matrix();
+			m.Multiply(Parent.WorldTransform);
+			m.TransformPoints(pts);
+			return pts;
+		}
+
+		/// <summary>
+		/// Converts one or more points in screen space to world space
+		/// </summary>
+		/// <param name="sceneTransform"></param>
+		/// <param name="pts"></param>
+		/// <returns></returns>
+		public virtual PointF[] ScreenToWorldPt(Matrix sceneTransform, params PointF[] pts)
+		{
+			Matrix m = new Matrix();
+			m.Multiply(sceneTransform);
+			m.Invert();
+			m.TransformPoints(pts);
+			return pts;
+		}
+
+		/// <summary>
+		/// Converts a point from world space to local space
+		/// </summary>
+		/// <param name="pt"></param>
+		/// <returns></returns>
+		public PointF WorldToLocalPt(PointF pt)
+		{
+			PointF[] pts = new PointF[] { pt };
+			Matrix m = new Matrix();
+			m.Multiply(WorldTransform);
+			m.Invert();
+			m.TransformPoints(pts);
+			return pts[0];
+		}
+
+		/// <summary>
+		/// Converts a point from screen space to local unscaled space
+		/// </summary>
+		/// <param name="x"></param>
+		/// <param name="y"></param>
+		/// <returns></returns>
+		public PointF[] ToLocalUnscaledPt(Matrix sceneTransform, params PointF[] pts)
+		{
+			Matrix m = new Matrix();
+			m.Multiply(sceneTransform);
+			m.Multiply(UnscaledWorldTransform);
+			m.Invert();
+			m.TransformPoints(pts);
+			return pts;
+		}
+
+		public float WorldAlpha
+		{
+			get
+			{
+				float alpha = 1;
+				LiveObject parent = this;
+				while (parent != null)
+				{
+					alpha *= parent.Alpha / 100.0f;
+					parent = parent.Parent;
+				}
+				return alpha * 100;
+			}
+		}
+		#endregion
+
+		public LiveObject Copy()
+		{
+			Type type = GetType();
+			LiveObject copy = Activator.CreateInstance(type) as LiveObject;
+			copy.CenterX = CenterX;
+			copy.CenterY = CenterY;
+			CopyPropertiesInto(copy);
+			OnCopyTo(copy);
+			copy.Parent = Parent;
+			copy.UpdateLocalTransform();
+			return copy;
+		}
+		protected virtual void OnCopyTo(LiveObject copy)
+		{
+		}
+
+		/// <summary>
+		/// Adds a property value to a keyframe at the given time
+		/// </summary>
+		/// <param name="time">Time in seconds from start </param>
+		/// <param name="propName"></param>
+		/// <param name="serializedValue"></param>
+		/// <returns>Keyframe at that point</returns>
+		public void AddValue<T>(float time, string propName, string serializedValue)
+		{
+			AddValue<T>(time, propName, serializedValue, false);
+		}
+
+		/// <summary>
+		/// Adds a property value to a keyframe at the given time
+		/// </summary>
+		/// <param name="time">Time in seconds from start </param>
+		/// <param name="propName"></param>
+		/// <param name="serializedValue"></param>
+		/// <returns>Keyframe at that point</returns>
+		protected virtual void AddValue<T>(float time, string propName, string serializedValue, bool addAnimBreak)
+		{
+		}
+
+		public void InvalidatePreview()
+		{
+			PreviewInvalidated?.Invoke(this, EventArgs.Empty);
+		}
+		public abstract LiveObject CreateLivePreview(float time);
+		public abstract void DestroyLivePreview();
+
+		public abstract void UpdateRealTime(float deltaTime, bool inPlayback);
+		public abstract void Update(float time, float elapsedTime, bool inPlayback);
+
+		public abstract void Draw(Graphics g, Matrix sceneTransform, List<string> markers, bool inPlayback);
+		public virtual void DrawSelection(Graphics g, Matrix sceneTransform, CanvasState editState, HoverContext hoverContext)
+		{
+			int midX = Width / 2;
+			int midY = Height / 2;
+			PointF[] localPts = new PointF[] {
+				new PointF(0,0),
+				new PointF(Width, 0),
+				new PointF(Width, Height),
+				new PointF(0, Height),
+				new PointF(PivotX * Width, PivotY * Height), //index 4: pivot
+			};
+			PointF[] boundPts = ToScreenPt(sceneTransform, localPts);
+			PointF[] outerPts = new PointF[4];
+			for (int i = 0; i < 4; i++)
+			{
+				outerPts[i] = boundPts[i];
+			}
+
+			DrawSelectionBox(g, outerPts);
+			DrawHandles(g, outerPts);
+
+			//pivot point
+			if (CanPivot)
+			{
+				if (editState == CanvasState.MovingPivot || hoverContext == HoverContext.Pivot)
+				{
+					g.MultiplyTransform(UnscaledWorldTransform);
+					g.MultiplyTransform(sceneTransform, MatrixOrder.Append);
+					g.DrawRectangle(_penPivotBox, 0, 0, Width, Height);
+					g.ResetTransform();
+				}
+
+				PointF pt = localPts[4];
+				g.FillEllipse(Brushes.White, pt.X - 3, pt.Y - 3, 6, 6);
+				g.FillEllipse(Brushes.Black, pt.X - 2, pt.Y - 2, 4, 4);
+			}
+		}
+
+		protected void DrawSelectionBox(Graphics g, params PointF[] vertices)
+		{
+			g.DrawPolygon(_penOuterSelection, vertices);
+			g.DrawPolygon(_penInnerSelection, vertices);
+		}
+
+		protected void DrawHandles(Graphics g, params PointF[] vertices)
+		{
+			//Grab handles
+			if (CanScale)
+			{
+				for (int i = 0; i < vertices.Length; i++)
+				{
+					PointF pt = vertices[i];
+					PointF next = i < vertices.Length - 1 ? vertices[i + 1] : vertices[0];
+					PointF mid = new PointF((pt.X + next.X) / 2, (pt.Y + next.Y) / 2);
+					g.FillRectangle(_brushHandle, mid.X - 3, mid.Y - 3, 6, 6);
+				}
+			}
+		}
+
+		public bool HiddenByMarker(List<string> markers)
+		{
+			if (markers != null && !string.IsNullOrEmpty(MarkerName))
+			{
+				switch (MarkerOp)
+				{
+					case MarkerOperator.NotEqual:
+					case MarkerOperator.LessThan:
+					case MarkerOperator.GreaterThan:
+						if (markers.Contains(MarkerName) && MarkerValue != "0" || !markers.Contains(MarkerName) && MarkerValue == "0")
+						{
+							return true;
+						}
+						break;
+					default:
+						if (markers.Contains(MarkerName) && MarkerValue == "0" || !markers.Contains(MarkerName) && MarkerValue != "0")
+						{
+							return true;
+						}
+						break;
+				}
+			}
+			return false;
+		}
+
+		#region Point-and-click editing
+		public virtual bool CanTranslate { get { return true; } }
+		public virtual bool CanRotate { get { return true; } }
+		public virtual bool CanScale { get { return true; } }
+		public virtual bool CanSkew { get { return true; } }
+		public virtual bool CanPivot { get { return true; } }
+		public virtual bool CanArrow { get { return false; } }
+
+		/// <summary>
+		/// Sets the object's local position so that its world position is at the given value
+		/// </summary>
+		/// <param name="x"></param>
+		/// <param name="y"></param>
+		public void SetWorldPosition(PointF worldPos, Matrix sceneTransform)
+		{
+			PointF local = Parent == null ? worldPos : Parent.WorldToLocalPt(worldPos);
+			Translate(local.X, local.Y, sceneTransform);
+		}
+
+		/// <summary>
+		/// Updates the sprite's position to a new value, updating the underlying data structures too
+		/// </summary>
+		/// <returns>List of objects that were modified</returns>
+		public virtual void Translate(float x, float y, Matrix sceneTransform)
+		{
+			if (X == x && Y == y)
+			{
+				return;
+			}
+
+			x = (float)Math.Round(x, 0);
+			y = (float)Math.Round(y, 0);
+
+			float time = GetRelativeTime();
+			AddValue<float>(time, "X", x.ToString(CultureInfo.InvariantCulture));
+			AddValue<float>(time, "Y", y.ToString(CultureInfo.InvariantCulture));
+		}
+
+		public void AdjustPivot(PointF screenPt, Matrix sceneTransform)
+		{
+			PointF[] pts = new PointF[] {
+				screenPt
+			};
+			PointF[] localPts = ToLocalUnscaledPt(sceneTransform, pts);
+			PointF localPt = localPts[0];
+			float xPct = localPt.X / Width;
+			float yPct = localPt.Y / Height;
+
+			float pivotX = (float)Math.Round(xPct, 2);
+			float pivotY = (float)Math.Round(yPct, 2);
+			if (pivotX == PivotX && pivotY == PivotY)
+			{
+				return;
+			}
+			PivotX = xPct;
+			PivotY = yPct;
+		}
+
+		public virtual void Scale(Point screenPoint, Matrix sceneTransform, HoverContext context)
+		{
+			float time = GetRelativeTime();
+			bool horizontal = (context & HoverContext.ScaleHorizontal) != 0;
+			bool vertical = (context & HoverContext.ScaleVertical) != 0;
+
+			//scale is determined by first converting point to local space
+			PointF localPt = ToLocalPt(sceneTransform, screenPoint)[0];
+			PointF pivotPt = new PointF(PivotX * Width, PivotY * Height);
+
+			float scaleX = ScaleX;
+			float scaleY = ScaleY;
+
+			if (context.HasFlag(HoverContext.ScaleRight))
+			{
+				float scaledDist = Width - pivotPt.X;
+				float distFromPivot = localPt.X - pivotPt.X;
+				float unscaledDist = scaledDist / ScaleX;
+				scaleX = distFromPivot / unscaledDist;
+			}
+			else if (context.HasFlag(HoverContext.ScaleLeft))
+			{
+				float scaledDist = pivotPt.X;
+				float distFromPivot = pivotPt.X - localPt.X;
+				float unscaledDist = scaledDist / ScaleX;
+				scaleX = distFromPivot / unscaledDist;
+			}
+			if (context.HasFlag(HoverContext.ScaleBottom))
+			{
+				float scaledDist = Height - pivotPt.Y;
+				float distFromPivot = localPt.Y - pivotPt.Y;
+				float unscaledDist = scaledDist / ScaleY;
+				scaleY = distFromPivot / unscaledDist;
+			}
+			else if (context.HasFlag(HoverContext.ScaleTop))
+			{
+				float scaledDist = pivotPt.Y;
+				float distFromPivot = pivotPt.Y - localPt.Y;
+				float unscaledDist = scaledDist / ScaleY;
+				scaleY = distFromPivot / unscaledDist;
+			}
+			if (horizontal && !float.IsInfinity(scaleX))
+			{
+				if (scaleX == 0)
+				{
+					scaleX = 0.01f;
+				}
+				scaleX = (float)Math.Round(scaleX, 2);
+				if (scaleX != ScaleX)
+				{
+					AddValue<float>(time, "ScaleX", scaleX.ToString(CultureInfo.InvariantCulture));
+				}
+			}
+			if (vertical && !float.IsInfinity(scaleY))
+			{
+				if (scaleY == 0)
+				{
+					scaleY = 0.01f;
+				}
+				scaleY = (float)Math.Round(scaleY, 2);
+				if (scaleY != ScaleY)
+				{
+					AddValue<float>(time, "ScaleY", scaleY.ToString(CultureInfo.InvariantCulture));
+				}
+			}
+		}
+
+		public void Rotate(Point screenPoint, PointF screenPivot, Point downPoint, float initialRotation)
+		{
+			double downAngle = Math.Atan2(screenPivot.Y - downPoint.Y, screenPivot.X - downPoint.X);
+			downAngle = downAngle * (180 / Math.PI) - 90;
+
+			double angle = Math.Atan2(screenPivot.Y - screenPoint.Y, screenPivot.X - screenPoint.X);
+			angle = angle * (180 / Math.PI) - 90;
+
+			angle -= downAngle;
+			double rotation = Math.Round(initialRotation + angle, 0);
+
+			if (Rotation == rotation)
+			{
+				return;
+			}
+
+			float time = GetRelativeTime();
+			Rotation = (float)rotation;
+			AddValue<float>(time, "Rotation", Rotation.ToString(CultureInfo.InvariantCulture));
+		}
+
+		public void Skew(Point screenPoint, Point downPoint, HoverContext context, float zoom)
+		{
+			float dx = (screenPoint.X - downPoint.X) / zoom;
+			float dy = (screenPoint.Y - downPoint.Y) / zoom;
+			switch (context)
+			{
+				case HoverContext.SkewLeft:
+					dy = -dy;
+					break;
+				case HoverContext.SkewRight:
+					break;
+				case HoverContext.SkewTop:
+					dx = -dx;
+					break;
+			}
+
+			float time = GetRelativeTime();
+
+			//skew formula: shift = size * tan(radians) / 2
+			//solved for angle: angle = atan(2 * shift / size)
+			if (HoverContext.SkewHorizontal.HasFlag(context))
+			{
+				//skewX
+				float skewX = (float)(Math.Atan(2 * dx / Height) * 180 / Math.PI);
+				AddValue<float>(time, "SkewX", skewX.ToString(CultureInfo.InvariantCulture));
+			}
+			else
+			{
+				//skewY
+				float skewY = (float)(Math.Atan(2 * dy / Width) * 180 / Math.PI);
+				AddValue<float>(time, "SkewY", skewY.ToString(CultureInfo.InvariantCulture));
+			}
+		}
+
+		public virtual void UpdateArrowPosition(HoverContext arrowContext) { }
+		#endregion
+
+		public virtual bool FilterRecord(string key)
+		{
+			return true;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveParticle.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveParticle.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b315f798bde9ad421b970e0c07a18594b7f61c11
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveParticle.cs	
@@ -0,0 +1,185 @@
+using SPNATI_Character_Editor.EpilogueEditing;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class LiveParticle : LiveObject
+	{
+		public LiveEmitter Emitter;
+		public float Duration;
+		public float Elapsed;
+		public float Spin;
+		public float InitialAngle;
+		public float SpeedX;
+		public float SpeedY;
+		public float Acceleration;
+		public float ForceX;
+		public float ForceY;
+
+		public TweenableParameter ScaleXTween;
+		public TweenableParameter ScaleYTween;
+		public TweenableParameter AlphaTween;
+		public TweenableColor ColorTween;
+		public TweenableParameter SpinTween;
+		public TweenableParameter SkewXTween;
+		public TweenableParameter SkewYTween;
+
+		public string Ease;
+		public int Layer;
+
+		private SolidBrush _brush = new SolidBrush(Color.White);
+
+		public LiveParticle(LiveEmitter emitter)
+		{
+			Emitter = emitter;
+			CenterX = true;
+			CenterY = true;
+
+			//randomize the rotation by the emission angle range
+			float rotation = emitter.Rotation;
+			float angle = emitter.Random.Next(-(int)emitter.Angle, (int)emitter.Angle + 1);
+			rotation += angle;
+
+			X = emitter.X;
+			Y = emitter.Y;
+
+			Image = emitter.Image;
+			Width = emitter.ParticleWidth;
+			Height = emitter.ParticleHeight;
+			PivotX = Width / 2.0f;
+			PivotY = Height / 2.0f;
+
+			Rotation = emitter.IgnoreRotation ? 0 : rotation;
+			float degrees = rotation;
+			float radians = degrees * (float)Math.PI / 180.0f;
+			float speed = (emitter.Speed ?? new RandomParameter(0, 0)).Get();
+
+			InitialAngle = radians;
+
+			//convert rotation angle to direction vector where 0 deg = [0,-1], 90 deg = [1,0]
+			float u = (float)Math.Sin(radians);
+			float v = -(float)Math.Cos(radians);
+
+			SpeedX = speed * u;
+			SpeedY = speed * v;
+
+			Acceleration = (emitter.Acceleration ?? new RandomParameter(0, 0)).Get();
+			ForceX = (emitter.ForceX ?? new RandomParameter(0, 0)).Get();
+			ForceY = (emitter.ForceY ?? new RandomParameter(0, 0)).Get();
+
+			Ease = emitter.ParticleEase;
+
+			PivotX = 0.5f;
+			PivotY = 0.5f;
+
+			ScaleXTween = new TweenableParameter((emitter.StartScaleX ?? new RandomParameter(1, 1)).Get(), (emitter.EndScaleX ?? emitter.StartScaleX ?? new RandomParameter(1, 1)).Get());
+			ScaleYTween = new TweenableParameter((emitter.StartScaleY ?? new RandomParameter(1, 1)).Get(), (emitter.EndScaleY ?? emitter.StartScaleY ?? new RandomParameter(1, 1)).Get());
+			AlphaTween = new TweenableParameter((emitter.StartAlpha ?? new RandomParameter(100, 100)).Get(), (emitter.EndAlpha ?? emitter.StartAlpha ?? new RandomParameter(0, 0)).Get());
+			ColorTween = new TweenableColor((emitter.StartColor ?? new RandomColor(Color.White, Color.White)).Get(), (emitter.EndColor ?? emitter.StartColor ?? new RandomColor(Color.White, Color.White)).Get());
+			SpinTween = new TweenableParameter((emitter.StartRotation ?? new RandomParameter(0, 0)).Get(), (emitter.EndRotation ?? emitter.StartRotation ?? new RandomParameter(0, 0)).Get());
+			SkewXTween = new TweenableParameter((emitter.StartSkewX ?? new RandomParameter(0, 0)).Get(), (emitter.EndSkewX ?? emitter.StartSkewX ?? new RandomParameter(0, 0)).Get());
+			SkewYTween = new TweenableParameter((emitter.StartSkewY ?? new RandomParameter(0, 0)).Get(), (emitter.EndSkewY ?? emitter.StartSkewY ?? new RandomParameter(0, 0)).Get());
+
+			Layer = emitter.Z;
+
+			Elapsed = 0;
+			Duration = (emitter.Lifetime ?? new RandomParameter(1, 1)).Get() * 1000;
+			UpdateRealTime(0, false);
+		}
+
+		public override bool IsVisible
+		{
+			get { return true; }
+		}
+
+		public override LiveObject CreateLivePreview(float time)
+		{
+			throw new NotImplementedException();
+		}
+
+		public override ITimelineWidget CreateWidget(Timeline timeline)
+		{
+			throw new NotImplementedException();
+		}
+
+		public override void DestroyLivePreview()
+		{
+			throw new NotImplementedException();
+		}
+
+		public override void Draw(Graphics g, Matrix sceneTransform, List<string> markers, bool inPlayback)
+		{
+			g.MultiplyTransform(WorldTransform);
+			g.MultiplyTransform(sceneTransform, MatrixOrder.Append);
+
+			if (Image != null)
+			{
+				g.DrawImage(Image, 0, 0, Width, Height);
+			}
+			else
+			{
+				g.FillEllipse(_brush, 0, 0, Width, Height);
+			}
+
+			g.ResetTransform();
+		}
+
+		public override void Update(float time, float elapsedTime, bool inPlayback)
+		{
+
+		}
+
+		public bool IsDead
+		{
+			get
+			{
+				return Elapsed >= Duration;
+			}
+		}
+
+		public override void UpdateRealTime(float deltaTime, bool inPlayback)
+		{
+			Elapsed += deltaTime;
+
+			if (IsDead)
+			{
+				return;
+			}
+
+			float elapsedSec = deltaTime / 1000.0f;
+			float dt = elapsedSec;
+
+			float t = Math.Min(1, Elapsed / Duration);
+			t = AnimationHelpers.Ease(Ease, t);
+			ScaleX = ScaleXTween.Tween(t);
+			ScaleY = ScaleYTween.Tween(t);
+			SkewX = SkewXTween.Tween(t);
+			SkewY = SkewYTween.Tween(t);
+			_brush.Color = System.Drawing.Color.FromArgb((int)(AlphaTween.Tween(t) / 100.0 * 255.0f), ColorTween.Tween(t));
+			Alpha = AlphaTween.Value;
+			Spin = SpinTween.Tween(t);
+
+			Rotation += Spin * dt;
+
+			//accelerate in the forward direction
+			float forward = InitialAngle;
+			float u = (float)Math.Sin(forward);
+			float v = -(float)Math.Cos(forward);
+
+			var accelX = Acceleration * u;
+			var accelY = Acceleration * v;
+
+			SpeedX += (accelX + ForceX) * dt;
+			SpeedY += (accelY + ForceY) * dt;
+
+			X += dt * SpeedX;
+			Y += dt * SpeedY;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LivePose.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LivePose.cs
index 333e5db797ca7b0f9db70d794efc6ea2f8efa0b2..89566a95c9be81acc30d54ff2ae185fb677659c7 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LivePose.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LivePose.cs	
@@ -1,15 +1,15 @@
-using Desktop;
-using Desktop.CommonControls.PropertyControls;
-using Desktop.DataStructures;
-using System;
+using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Drawing;
+using System.Drawing.Drawing2D;
 using System.IO;
+using Desktop;
+using Desktop.CommonControls.PropertyControls;
 
 namespace SPNATI_Character_Editor.EpilogueEditor
 {
-	public class LivePose : BindableObject, ITimelineData, ILabel
+	public class LivePose : LiveData, ILabel
 	{
 		public ISkin Character;
 		public Pose Pose;
@@ -34,8 +34,8 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			}
 		}
 
-		[Numeric(DisplayName = "Base Height", Key = "baseHeight", GroupOrder = 10, Minimum = 0, Maximum = 50000)]
-		public int BaseHeight
+		[Numeric(DisplayName = "Base Height", Key = "baseHeight", GroupOrder = 10, Minimum = 1, Maximum = 50000)]
+		public override int BaseHeight
 		{
 			get { return Get<int>(); }
 			set { Set(value); }
@@ -43,6 +43,10 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		private float _time;
 
+		public LivePose()
+		{
+			Sprites = new ObservableCollection<LiveSprite>();
+		}
 		public LivePose(ISkin character, Pose pose)
 		{
 			Character = character;
@@ -50,6 +54,26 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			ConvertPose(pose);
 		}
 
+		public override LiveObject Find(string id)
+		{
+			return Sprites.Find(s => s.Id == id);
+		}
+
+		public override void FitScene(int windowWidth, int windowHeight, ref Point offset, ref float zoom)
+		{
+			offset = new Point(0, 0);
+			zoom = 1;
+		}
+
+		public override Matrix GetSceneTransform(int width, int height, Point offset, float zoom)
+		{
+			Matrix transform = new Matrix();
+			float screenScale = height * zoom / BaseHeight;
+			transform.Scale(screenScale, screenScale, MatrixOrder.Append); // scale to display * zoom
+			transform.Translate(width * 0.5f + offset.X, offset.Y, MatrixOrder.Append); // center horizontally
+			return transform;
+		}
+
 		/// <summary>
 		/// Converts a Pose definition into a LivePose
 		/// </summary>
@@ -102,7 +126,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 						sprites[preview.Id] = preview;
 					}
 				}
-				preview.AddDirective(directive);
+				preview.AddKeyframeDirective(directive, 0);
 			}
 		}
 
@@ -123,7 +147,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		/// <returns></returns>
 		public LiveSprite AddSprite(LiveSprite sprite, int index)
 		{
-			sprite.Pose = this;
+			sprite.Data = this;
 			sprite.PropertyChanged += Sprite_PropertyChanged;
 			if (index == -1)
 			{
@@ -180,11 +204,37 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			});
 		}
 
-		public event EventHandler<WidgetCreationArgs> WidgetMoved;
-		public event EventHandler<WidgetCreationArgs> WidgetCreated;
-		public event EventHandler<WidgetCreationArgs> WidgetRemoved;
+		public override event EventHandler<WidgetCreationArgs> WidgetMoved;
+		public override event EventHandler<WidgetCreationArgs> WidgetCreated;
+		public override event EventHandler<WidgetCreationArgs> WidgetRemoved;
+
+		/// <summary>
+		/// Gets the topmost object beneath the given screen coordinate
+		/// </summary>
+		/// <param name="x"></param>
+		/// <param name="y"></param>
+		/// <param name="objects"></param>
+		/// <returns></returns>
+		public override LiveObject GetObjectAtPoint(int x, int y, Matrix sceneTransform, bool ignoreMarkers, List<string> markers)
+		{
+			//search in reverse order because objects are sorted by depth
+			for (int i = DrawingOrder.Count - 1; i >= 0; i--)
+			{
+				LiveObject obj = DrawingOrder[i];
+				if (!obj.IsVisible || obj.Hidden || obj.Alpha == 0 || obj.HiddenByMarker(ignoreMarkers ? null : markers)) { continue; }
+
+				//transform point to local space
+				PointF localPt = obj.ToLocalPt(x, y, sceneTransform);
+				if (localPt.X >= 0 && localPt.X <= obj.Width &&
+					localPt.Y >= 0 && localPt.Y <= obj.Height)
+				{
+					return obj;
+				}
+			}
+			return null;
+		}
 
-		public List<ITimelineWidget> CreateWidgets(Timeline timeline)
+		public override List<ITimelineWidget> CreateWidgets(Timeline timeline)
 		{
 			List<ITimelineWidget> list = new List<ITimelineWidget>();
 			for (int i = 0; i < Sprites.Count; i++)
@@ -195,7 +245,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			return list;
 		}
 
-		public ITimelineWidget CreateWidget(Timeline timeline, float time, object context)
+		public override ITimelineWidget CreateWidget(Timeline timeline, float time, object context)
 		{
 			LiveSprite sprite = AddSprite(time);
 			SpriteWidget widget = new SpriteWidget(sprite, timeline);
@@ -228,16 +278,42 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			return id;
 		}
 
-		public ITimelineWidget CreateWidget(Timeline timeline, float time, object data, int index)
+		public override ITimelineWidget CreateWidget(Timeline timeline, float time, object data, int index)
 		{
 			LiveSprite sprite = data as LiveSprite;
-			sprite.Id += "(copy)";
+			sprite.Id = GetCopyId(sprite.Id);
 			AddSprite(sprite, index);
 			SpriteWidget widget = new SpriteWidget(sprite, timeline);
 			return widget;
 		}
 
-		public void MoveWidget(ITimelineWidget widget, int track)
+		private string GetCopyId(string id)
+		{
+			HashSet<string> ids = new HashSet<string>();
+			foreach (LiveSprite sprite in Sprites)
+			{
+				ids.Add(sprite.Id);
+			}
+			string prefix = id;
+			string newId = id;
+			int suffix = 0;
+			while (ids.Contains(newId))
+			{
+				if (prefix == id)
+				{
+					prefix += "(copy)";
+					newId = prefix;
+				}
+				else
+				{
+					++suffix;
+					newId = prefix + suffix;
+				}
+			}
+			return newId;
+		}
+
+		public override void MoveWidget(ITimelineObject widget, int track)
 		{
 			if (widget is SpriteWidget)
 			{
@@ -257,7 +333,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			WidgetMoved?.Invoke(this, new WidgetCreationArgs(widget, track));
 		}
 
-		public void InsertWidget(ITimelineWidget widget, float time, int index)
+		public override void InsertWidget(ITimelineObject widget, float time, int index)
 		{
 			if (widget is SpriteWidget)
 			{
@@ -278,7 +354,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			WidgetCreated?.Invoke(this, new WidgetCreationArgs(widget, index));
 		}
 
-		public int RemoveWidget(ITimelineWidget widget)
+		public override int RemoveWidget(ITimelineObject widget)
 		{
 			if (widget is SpriteWidget)
 			{
@@ -296,9 +372,9 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			return -1;
 		}
 
-		public void UpdateSelection(WidgetSelectionArgs args)
+		public override void UpdateSelection(WidgetSelectionArgs args)
 		{
-			object clipboardData = Clipboards.Get<SpriteWidget, object>();
+			object clipboardData = Clipboards.Get<KeyframedWidget, object>();
 			args.AllowCut = false;
 			args.AllowCopy = false;
 			args.AllowDelete = false;
@@ -311,18 +387,41 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		}
 
 		#region Drawing
-		public void UpdateTime(float time, bool inPlayback)
+		public override void UpdateTime(float time, float elapsedTime, bool inPlayback)
 		{
 			_time = time;
 			foreach (LiveSprite sprite in Sprites)
 			{
-				sprite.Update(time, inPlayback);
+				sprite.Update(time, elapsedTime, inPlayback);
+			}
+		}
+
+		public override void UpdateRealTime(float deltaTime, bool inPlayback)
+		{
+
+		}
+
+		public override void Draw(Graphics g, Matrix sceneTransform, List<string> markers, LiveObject selectedObject, LiveObject selectedPreview, bool inPlayback)
+		{
+			foreach (LiveSprite sprite in DrawingOrder)
+			{
+				sprite.Draw(g, sceneTransform, markers, inPlayback);
+				if (selectedObject == sprite && !selectedObject.Hidden && selectedPreview != null)
+				{
+					selectedPreview.Draw(g, sceneTransform, markers, inPlayback);
+				}
 			}
 		}
+		#endregion
 
-		public bool Paste(WidgetOperationArgs args, int index)
+		public override bool Paste(WidgetOperationArgs args, LiveObject after)
 		{
-			LiveSprite clipboardData = Clipboards.Get<SpriteWidget, LiveSprite>();
+			int index = -1;
+			if (after != null)
+			{
+				index = Sprites.IndexOf(after as LiveSprite) + 1;
+			}
+			LiveSprite clipboardData = Clipboards.Get<KeyframedWidget, LiveSprite>();
 			if (clipboardData != null)
 			{
 				args.Timeline.CreateWidget(clipboardData.Copy(), index);
@@ -331,10 +430,46 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			return false;
 		}
 
-		public bool OnPaste(WidgetOperationArgs args)
+		public override bool OnPaste(WidgetOperationArgs args)
+		{
+			return Paste(args, null);
+		}
+
+		public override List<LiveObject> GetAvailableParents(LiveObject child)
+		{
+			List<LiveObject> list = new List<LiveObject>();
+			foreach (LiveSprite sprite in Sprites)
+			{
+				if (string.IsNullOrEmpty(sprite.Id) || sprite == child)
+				{
+					continue;
+				}
+				//if this is an ancestor of the sprite, disallow it to avoid infinite chains
+				LiveObject parent = sprite.Parent;
+				bool isAncestor = false;
+				while (parent != null)
+				{
+					if (parent == child)
+					{
+						isAncestor = true;
+						break;
+					}
+					parent = parent.Parent as LiveSprite;
+				}
+				if (!isAncestor)
+				{
+					list.Add(sprite);
+				}
+			}
+			list.Sort();
+			return list;
+		}
+
+		public override List<ITimelineBreak> CreateBreaks(Timeline timeline)
 		{
-			return Paste(args, -1);
+			List<ITimelineBreak> list = new List<ITimelineBreak>();
+			return list;
 		}
-		#endregion;
+		public override ITimelineBreak AddBreak(float time) { throw new NotImplementedException(); }
 	}
 }
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveScene.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveScene.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2dec515576a5689a9aec54c15b2c14cedd2fd8a5
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveScene.cs	
@@ -0,0 +1,843 @@
+using Desktop;
+using Desktop.CommonControls.PropertyControls;
+using SPNATI_Character_Editor.Controls;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	/// <summary>
+	/// Editable scene
+	/// </summary>
+	public class LiveScene : LiveData, ICanvasViewport, ILabel
+	{
+		public const float MinZoom = 0.25f;
+		public const float MaxZoom = 3;
+
+		public ObservableCollection<LiveBreak> Breaks
+		{
+			get { return Get<ObservableCollection<LiveBreak>>(); }
+			set { Set(value); }
+		}
+
+		public ObservableCollection<LiveObject> Tracks
+		{
+			get { return Get<ObservableCollection<LiveObject>>(); }
+			set { Set(value); }
+		}
+		public LiveCamera Camera
+		{
+			get { return Get<LiveCamera>(); }
+			set { Set(value); }
+		}
+
+		public bool LockToCamera
+		{
+			get { return Camera.BlockOutsideView; }
+			set { Camera.BlockOutsideView = value; }
+		}
+
+		public List<LiveObject> DrawingOrder = new List<LiveObject>();
+
+		public Character Character { get; set; }
+
+		public override event EventHandler<WidgetCreationArgs> WidgetMoved;
+		public override event EventHandler<WidgetCreationArgs> WidgetCreated;
+		public override event EventHandler<WidgetCreationArgs> WidgetRemoved;
+		public event EventHandler ViewportUpdated;
+		public event EventHandler LabelChanged;
+
+		[Text(DisplayName = "Name", GroupOrder = 0, Description = "Scene name")]
+		public string Name
+		{
+			get { return Get<string>(); }
+			set { Set(value); LabelChanged?.Invoke(this, EventArgs.Empty); }
+		}
+
+		private Image _background;
+		[FileSelect(DisplayName = "Background", GroupOrder = 5, Description = "Scene background image")]
+		public string BackgroundImage
+		{
+			get { return Get<string>(); }
+			set
+			{
+				Set(value);
+				_background = LiveImageCache.Get(value);
+				if (_background != null)
+				{
+					Width = _background.Width;
+					Height = _background.Height;
+				}
+			}
+		}
+
+		private SolidBrush _backColor = new SolidBrush(Color.Gray);
+		[Color(DisplayName = "Background Color", GroupOrder = 10, Description = "Scene background color")]
+		public Color BackColor
+		{
+			get { return Get<Color>(); }
+			set
+			{
+				if (_backColor != null)
+				{
+					_backColor.Dispose();
+				}
+				Set(value);
+				_backColor = new SolidBrush(value);
+			}
+		}
+
+		[Numeric(DisplayName = "Width", GroupOrder = 15, Description = "Scene width in pixels when at full scale", Minimum = 100, Maximum = 2000)]
+		public int Width
+		{
+			get { return Get<int>(); }
+			set { Set(value); }
+		}
+
+		[Numeric(DisplayName = "Height", GroupOrder = 16, Description = "Scene height in pixels when at full scale", Minimum = 100, Maximum = 2000)]
+		public int Height
+		{
+			get { return Get<int>(); }
+			set { Set(value); }
+		}
+
+		private float _time;
+		private float _elapsedTime;
+
+		public float AspectRatio { get { return Width / (float)Height; } }
+
+		public LiveScene()
+		{
+			Width = 100;
+			Height = 100;
+			Camera = new LiveCamera();
+			Tracks = new ObservableCollection<LiveObject>();
+			Breaks = new ObservableCollection<LiveBreak>();
+		}
+
+		public LiveScene(Scene scene, Character character) : this()
+		{
+			Character = character;
+			Name = scene.Name ?? "New scene";
+			if (!string.IsNullOrEmpty(scene.BackgroundColor))
+			{
+				BackColor = ColorTranslator.FromHtml(scene.BackgroundColor);
+			}
+			if (!string.IsNullOrEmpty(scene.Background))
+			{
+				BackgroundImage = character.FolderName + "/" + scene.Background;
+			}
+			Camera = new LiveCamera(scene);
+			Camera.PropertyChanged += Camera_PropertyChanged;
+			Tracks.Add(Camera);
+
+			Tracks.CollectionChanged += Objects_CollectionChanged;
+
+			foreach (Directive directive in scene.Directives)
+			{
+				switch (directive.DirectiveType)
+				{
+					case "sprite":
+						LiveSprite sprite = new LiveSprite(this, directive, Character, _time);
+						sprite.PropertyChanged += Sprite_PropertyChanged;
+						Tracks.Add(sprite);
+						break;
+					case "move":
+					case "camera":
+						AddMoveDirective(directive);
+						break;
+					case "text":
+						LiveBubble bubble = new LiveBubble(this, directive, _time);
+						Tracks.Add(bubble);
+						break;
+					case "wait":
+					case "pause":
+						AddPauseDirective(directive);
+						break;
+				}
+			}
+			_time = 0;
+			_elapsedTime = 0;
+		}
+
+		private void Camera_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			if (LockToCamera)
+			{
+				ViewportUpdated?.Invoke(this, EventArgs.Empty);
+			}
+		}
+
+		protected override void OnPropertyChanged(string propName)
+		{
+			if (propName == "Width" && Camera != null)
+			{
+				Camera.Width = Width;
+				if (Camera.LinkedPreview != null)
+				{
+					Camera.LinkedPreview.Width = Width;
+				}
+			}
+			if (propName == "Height" && Camera != null)
+			{
+				Camera.Height = Height;
+				if (Camera.LinkedPreview != null)
+				{
+					Camera.LinkedPreview.Height = Height;
+				}
+			}
+		}
+
+		private void AddMoveDirective(Directive directive)
+		{
+			LiveAnimatedObject obj = null;
+			if (directive.Id == "camera")
+			{
+				obj = Camera;
+			}
+			else
+			{
+				obj = Tracks.Find(o => o.Id == directive.Id) as LiveAnimatedObject;
+			}
+			if (obj == null) { return; }
+
+			//create a keyframe from the directive
+			if (directive.Keyframes.Count == 0)
+			{
+				LiveKeyframe firstFrame;
+				obj.AddKeyframe(directive, _time, false, out firstFrame);
+			}
+
+			//add the directive's keyframes
+			obj.AddKeyframeDirective(directive, _time);
+		}
+
+		private void AddPauseDirective(Directive directive)
+		{
+			//update the current start basis to match the end time of the last non-looping animation
+			float max = Math.Max(GetEnd(Camera), _time);
+			foreach (LiveObject obj in Tracks)
+			{
+				LiveAnimatedObject animObj = obj as LiveAnimatedObject;
+				if (animObj == null)
+				{
+					continue;
+				}
+				float end = GetEnd(animObj);
+				max = Math.Max(end, max);
+			}
+			_time = Math.Max(max, _time);
+			if (directive.DirectiveType == "pause")
+			{
+				LiveBreak brk = new LiveBreak();
+				brk.Data = this;
+				brk.Time = _time;
+				Breaks.Add(brk);
+			}
+		}
+
+		private static float GetEnd(LiveAnimatedObject obj)
+		{
+			float end = obj.Start;
+			if (obj.Keyframes.Count == 0)
+			{
+				end += obj.Time;
+			}
+			else
+			{
+				foreach (string prop in obj.AnimatedProperties)
+				{
+					for (int i = obj.Keyframes.Count - 1; i >= 0; i--)
+					{
+						LiveKeyframe kf = obj.Keyframes[i];
+						LiveKeyframeMetadata metadata = obj.GetBlockMetadata(prop, kf.Time);
+						if (!metadata.Looped)
+						{
+							if (kf.HasProperty(prop))
+							{
+								end = Math.Max(end, obj.Start + kf.Time);
+								break;
+							}
+						}
+					}
+				}
+			}
+
+			return end;
+		}
+
+		public override int BaseHeight
+		{
+			get { return Height; }
+			set { Height = value; }
+		}
+
+		public bool AllowPan
+		{
+			get { return !LockToCamera; }
+		}
+
+		private void Sprite_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			if (e.PropertyName == "Z")
+			{
+				ReorderObjects();
+			}
+		}
+
+		private void Objects_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+		{
+			ReorderObjects();
+		}
+
+		private void ReorderObjects()
+		{
+			Dictionary<LiveObject, int> order = new Dictionary<LiveObject, int>();
+			DrawingOrder.Clear();
+			for (int i = 0; i < Tracks.Count; i++)
+			{
+				order[Tracks[i]] = i;
+				LiveObject obj = Tracks[i];
+				if (obj is LiveSprite || obj is LiveEmitter)
+				{
+					DrawingOrder.Add(obj);
+				}
+			}
+			DrawingOrder.Sort((s1, s2) =>
+			{
+				int compare = s1.Z.CompareTo(s2.Z);
+				if (compare == 0)
+				{
+					compare = order[s1].CompareTo(order[s2]);
+				}
+				return compare;
+			});
+		}
+
+		public override void Draw(Graphics g, Matrix sceneTransform, List<string> markers, LiveObject selectedObject, LiveObject selectedPreview, bool inPlayback)
+		{
+			g.FillRectangle(_backColor, 0, 0, g.VisibleClipBounds.Width, g.VisibleClipBounds.Height);
+
+			if (_background != null)
+			{
+				g.MultiplyTransform(sceneTransform);
+				g.DrawImage(_background, 0, 0);
+				g.ResetTransform();
+			}
+
+			foreach (LiveObject obj in DrawingOrder)
+			{
+				obj.Draw(g, sceneTransform, markers, inPlayback);
+				if (selectedObject == obj && !selectedObject.Hidden && selectedPreview != null)
+				{
+					selectedPreview.Draw(g, sceneTransform, markers, inPlayback);
+				}
+			}
+
+			//camera bounds
+			Camera.Draw(g, sceneTransform, markers, inPlayback);
+
+			//textboxes
+			g.MultiplyTransform(Camera.WorldTransform);
+			g.MultiplyTransform(sceneTransform, MatrixOrder.Append);
+			foreach (LiveObject obj in Tracks)
+			{
+				if (obj is LiveBubble)
+				{
+					obj.Draw(g, sceneTransform, markers, inPlayback);
+				}
+			}
+			g.ResetTransform();
+		}
+
+		public void FitToViewport(int windowWidth, int windowHeight, ref Point offset, ref float zoom)
+		{
+			//adjust zoom level
+			float aspectRatio = AspectRatio;
+			float viewWidth = aspectRatio * windowHeight;
+
+			int viewportHeight = 0;
+			if (viewWidth > windowWidth)
+			{
+				//take full width of window
+				viewportHeight = (int)(windowWidth / aspectRatio);
+			}
+			else
+			{
+				//take full height of window
+				viewportHeight = windowHeight;
+			}
+
+			//set the zoom to match the viewport height
+			float camZoom = Camera.Zoom;
+			zoom = viewportHeight * camZoom / Height;
+
+			//center on camera
+			int cx = (int)(Camera.X + Camera.Width / 2);
+			int cy = (int)(Camera.Y + Camera.Height / 2);
+
+			float camWidth = Camera.Width / Camera.Zoom;
+			float camHeight = Camera.Height / Camera.Zoom;
+
+			int scaledWidth = (int)(zoom * camWidth);
+			int scaledHeight = (int)(zoom * camHeight);
+
+			int x0 = (int)((cx - camWidth / 2) * zoom);
+			int y0 = (int)((cy - camHeight / 2) * zoom);
+
+			offset = new Point(-x0 + (windowWidth - scaledWidth) / 2, -y0 + (windowHeight - scaledHeight) / 2);
+		}
+
+		/// <summary>
+		/// Adjusts the zoom and offset to fit the scene on the screen
+		/// </summary>
+		/// <param name="windowWidth"></param>
+		/// <param name="windowHeight"></param>
+		/// <param name="offset"></param>
+		/// <param name="zoom"></param>
+		public override void FitScene(int windowWidth, int windowHeight, ref Point offset, ref float zoom)
+		{
+			if (LockToCamera)
+			{
+				FitToViewport(windowWidth, windowHeight, ref offset, ref zoom);
+				return;
+			}
+			offset = new Point(0, 0);
+
+			//adjust zoom level
+			float aspectRatio = AspectRatio;
+			float viewWidth = aspectRatio * windowHeight;
+
+			int viewportHeight = 0;
+			if (viewWidth > windowWidth)
+			{
+				//take full width of window
+				viewportHeight = (int)(windowWidth / aspectRatio);
+				offset = new Point(0, (windowHeight - (int)viewportHeight) / 2);
+			}
+			else
+			{
+				//take full height of window
+				viewportHeight = windowHeight;
+				offset = new Point((windowWidth - (int)viewWidth) / 2, 0);
+			}
+
+			zoom = (float)viewportHeight / Height;
+		}
+
+		public override Matrix GetSceneTransform(int windowWidth, int windowHeight, Point offset, float zoom)
+		{
+			Matrix transform = new Matrix();
+			float screenScale = zoom;
+			transform.Scale(screenScale, screenScale, MatrixOrder.Append); // scale to display * zoom
+			transform.Translate(offset.X, offset.Y, MatrixOrder.Append); // center horizontally
+			return transform;
+		}
+
+		public override LiveObject Find(string id)
+		{
+			return null;
+		}
+
+		public override LiveObject GetObjectAtPoint(int x, int y, Matrix sceneTransform, bool ignoreMarkers, List<string> markers)
+		{
+			LiveObject previewSource = Tracks.Find(t => t.LinkedPreview != null);
+			if (previewSource != null && !(previewSource is LiveBubble))
+			{
+				PointF localPt = previewSource.ToLocalPt(x, y, sceneTransform);
+				if (localPt.X >= 0 && localPt.X <= previewSource.Width &&
+					localPt.Y >= 0 && localPt.Y <= previewSource.Height)
+				{
+					return previewSource;
+				}
+			}
+
+			//search in reverse order because objects are sorted by depth
+			for (int i = Tracks.Count - 1; i >= 0; i--)
+			{
+				Matrix boxTransform = new Matrix();
+				boxTransform.Multiply(Camera.WorldTransform);
+				boxTransform.Multiply(sceneTransform, MatrixOrder.Append);
+				Point[] mousePt = new Point[] { new Point(x, y) };
+				boxTransform.Invert();
+				boxTransform.TransformPoints(mousePt);
+				LiveBubble bubble = Tracks[i] as LiveBubble;
+				if (bubble == null || !bubble.IsVisible || bubble.Hidden) { continue; }
+
+				if (bubble.Contains(mousePt[0], sceneTransform))
+				{
+					return bubble;
+				}
+			}
+
+			for (int i = DrawingOrder.Count - 1; i >= 0; i--)
+			{
+				LiveObject obj = DrawingOrder[i];
+				if (!obj.IsVisible || obj.Hidden || obj.Alpha == 0) { continue; }
+
+				//transform point to local space
+				PointF localPt = obj.ToLocalPt(x, y, sceneTransform);
+				if (localPt.X >= 0 && localPt.X <= obj.Width &&
+					localPt.Y >= 0 && localPt.Y <= obj.Height)
+				{
+					return obj;
+				}
+			}
+
+			//see if the camera is selected
+			PointF localToCam = Camera.ToLocalPt(x, y, sceneTransform);
+			if (localToCam.X >= 0 && localToCam.X <= Camera.Width && localToCam.Y >= 0 && localToCam.Y <= Camera.Height)
+			{
+				return Camera;
+			}
+			return null;
+		}
+
+		public override void UpdateTime(float time, float elapsedTime, bool inPlayback)
+		{
+			_time = time;
+			_elapsedTime = elapsedTime;
+			foreach (LiveObject obj in Tracks)
+			{
+				obj.Update(time, elapsedTime, inPlayback);
+			}
+		}
+
+		public override void UpdateRealTime(float deltaTime, bool inPlayback)
+		{
+			foreach (LiveObject sprite in Tracks)
+			{
+				sprite.UpdateRealTime(deltaTime, inPlayback);
+			}
+		}
+
+		public LiveSprite AddSprite(float time, string src)
+		{
+			LiveSprite sprite = new LiveSprite(this, time);
+			sprite.CenterX = false;
+			sprite.DisplayPastEnd = false;
+			sprite.PropertyChanged += Sprite_PropertyChanged;
+			Tracks.Add(sprite);
+			ReorderObjects();
+			return sprite;
+		}
+
+		public LiveBubble AddBubble(float time)
+		{
+			LiveBubble bubble = new LiveBubble(this, time);
+			bubble.Id = GetUniqueId("text");
+			Tracks.Add(bubble);
+			return bubble;
+		}
+
+		public LiveEmitter AddEmitter(float time)
+		{
+			LiveEmitter emitter = new LiveEmitter(this, time);
+			emitter.Id = GetUniqueId("emitter");
+			Tracks.Add(emitter);
+			ReorderObjects();
+			return emitter;
+		}
+
+		public override List<LiveObject> GetAvailableParents(LiveObject child)
+		{
+			List<LiveObject> list = new List<LiveObject>();
+			if (child is LiveBubble)
+			{
+				return list;
+			}
+			foreach (LiveObject obj in Tracks)
+			{
+				if (string.IsNullOrEmpty(obj.Id) || obj == child || obj is LiveBubble)
+				{
+					continue;
+				}
+
+				//disallow ancestors
+				LiveObject parent = obj.Parent;
+				bool isAncestor = false;
+				while (parent != null)
+				{
+					if (parent == child)
+					{
+						isAncestor = true;
+						break;
+					}
+					parent = parent.Parent;
+				}
+				if (!isAncestor)
+				{
+					list.Add(obj);
+				}
+			}
+			list.Sort();
+			return list;
+		}
+
+		public override List<ITimelineWidget> CreateWidgets(Timeline timeline)
+		{
+			List<ITimelineWidget> list = new List<ITimelineWidget>();
+			for (int i = 0; i < Tracks.Count; i++)
+			{
+				ITimelineWidget widget = Tracks[i].CreateWidget(timeline);
+				if (widget.IsCollapsible)
+				{
+					widget.IsCollapsed = true;
+				}
+				list.Add(widget);
+			}
+			return list;
+		}
+		public override ITimelineWidget CreateWidget(Timeline timeline, float time, object context)
+		{
+			if (context is LiveObject)
+			{
+				LiveObject obj = context as LiveObject;
+				ITimelineWidget widget = obj.CreateWidget(timeline);
+				if (widget.IsCollapsible)
+				{
+					widget.IsCollapsed = true;
+				}
+				return widget;
+			}
+
+			throw new NotSupportedException();
+		}
+
+		public string GetUniqueId(string id)
+		{
+			int suffix = 0;
+			string prefix = id;
+			while (Tracks.Find(s => s.Id == id) != null)
+			{
+				suffix++;
+				id = prefix + suffix;
+			}
+
+			return id;
+		}
+
+		public override ITimelineWidget CreateWidget(Timeline timeline, float time, object data, int index)
+		{
+			LiveObject obj = data as LiveObject;
+			if (obj != null)
+			{
+				obj.Id = GetCopyId(obj.Id);
+				AddObject(obj, index);
+				ITimelineWidget widget = obj.CreateWidget(timeline);
+				return widget;
+			}
+			throw new NotSupportedException("Cannot create a widget for " + data.GetType().Name);
+		}
+
+		private string GetCopyId(string id)
+		{
+			HashSet<string> ids = new HashSet<string>();
+			foreach (LiveObject obj in Tracks)
+			{
+				ids.Add(obj.Id);
+			}
+			string prefix = id;
+			string newId = id;
+			int suffix = 0;
+			while (ids.Contains(newId))
+			{
+				if (prefix == id)
+				{
+					prefix += "(copy)";
+					newId = prefix;
+				}
+				else
+				{
+					++suffix;
+					newId = prefix + suffix;
+				}
+			}
+			return newId;
+		}
+
+		public override List<ITimelineBreak> CreateBreaks(Timeline timeline)
+		{
+			List<ITimelineBreak> list = new List<ITimelineBreak>();
+			list.AddRange(Breaks);
+			return list;
+		}
+
+		public override ITimelineBreak AddBreak(float time)
+		{
+			//figure out the next appropriate break point based on the animations underway at this time
+			float breakTime = time;
+			foreach (LiveObject obj in Tracks)
+			{
+				LiveAnimatedObject anim = obj as LiveAnimatedObject;
+				if (anim != null)
+				{
+					if (anim.Keyframes.Count > 0)
+					{
+						foreach (string property in anim.Properties)
+						{
+							LiveKeyframe start;
+							LiveKeyframe end;
+							anim.GetBlock(property, time, out start, out end);
+							if (start != null && end != null && start.Time <= time && end.Time >= time && !start.GetMetadata(property, false).Looped)
+							{
+								breakTime = Math.Max(obj.Start + end.Time, breakTime);
+							}
+						}
+					}
+					else if (anim.Keyframes.Count == 1)
+					{
+						breakTime = Math.Max(obj.Start + anim.Length, time);
+					}
+				}
+			}
+			time = breakTime;
+
+			//see if there's already a breakpoint at this time and abort if so
+			LiveBreak existing = Breaks.Find(b => b.Time == time);
+			if (existing != null)
+			{
+				return null;
+			}
+
+			LiveBreak brk = new LiveBreak();
+			brk.Data = this;
+			brk.Time = time;
+			Breaks.Add(brk);
+			return brk;
+		}
+
+		/// <summary>
+		/// Adds a sprite to the end of the collection
+		/// </summary>
+		/// <param name="obj"></param>
+		/// <returns></returns>
+		private LiveObject AddObject(LiveObject obj, int index)
+		{
+			obj.Data = this;
+			obj.PropertyChanged += Sprite_PropertyChanged;
+			if (index == -1)
+			{
+				Tracks.Add(obj);
+			}
+			else
+			{
+				Tracks.Insert(index, obj);
+			}
+			return obj;
+		}
+
+		public override void InsertWidget(ITimelineObject widget, float time, int index)
+		{
+			if (widget is KeyframedWidget)
+			{
+				KeyframedWidget objWidget = widget as KeyframedWidget;
+				LiveObject data = objWidget.GetData() as LiveObject;
+				data.PropertyChanged -= Sprite_PropertyChanged;
+				if (index == -1)
+				{
+					Tracks.Add(data);
+					index = Tracks.Count - 1;
+				}
+				else
+				{
+					Tracks.Insert(index, data);
+				}
+			}
+			else if (widget is LiveBreak)
+			{
+				throw new NotImplementedException();
+			}
+			WidgetCreated?.Invoke(this, new WidgetCreationArgs(widget, index));
+		}
+		public override int RemoveWidget(ITimelineObject widget)
+		{
+			if (widget is KeyframedWidget)
+			{
+				KeyframedWidget objWidget = widget as KeyframedWidget;
+				LiveAnimatedObject data = objWidget.GetData() as LiveAnimatedObject;
+				int index = Tracks.IndexOf(data);
+				if (index >= 0)
+				{
+					Tracks.RemoveAt(index);
+					data.PropertyChanged -= Sprite_PropertyChanged;
+					WidgetRemoved?.Invoke(this, new WidgetCreationArgs(widget, index));
+				}
+				return index;
+			}
+			else if (widget is LiveBreak)
+			{
+				Breaks.Remove(widget as LiveBreak);
+				WidgetRemoved?.Invoke(this, new WidgetCreationArgs(widget, -1));
+			}
+			return -1;
+		}
+		public override void MoveWidget(ITimelineObject widget, int newTrack)
+		{
+			if (widget is ITimelineWidget)
+			{
+				LiveObject data = ((ITimelineWidget)widget).GetData() as LiveObject;
+				int index = Tracks.IndexOf(data);
+				Tracks.RemoveAt(index);
+				if (newTrack >= Tracks.Count || newTrack == -1)
+				{
+					Tracks.Add(data);
+				}
+				else
+				{
+					Tracks.Insert(newTrack, data);
+				}
+			}
+			else if (widget is LiveBreak)
+			{
+				throw new NotImplementedException();
+			}
+			WidgetMoved?.Invoke(this, new WidgetCreationArgs(widget, newTrack));
+		}
+		public override bool OnPaste(WidgetOperationArgs args)
+		{
+			return Paste(args, null);
+		}
+		public override void UpdateSelection(WidgetSelectionArgs args)
+		{
+			object clipboardData = Clipboards.Get<KeyframedWidget, object>();
+			args.AllowCut = false;
+			args.AllowCopy = false;
+			args.AllowDelete = false;
+			args.AllowDuplicate = false;
+			args.AllowPaste = false;
+			if (clipboardData is LiveAnimatedObject)
+			{
+				args.AllowPaste = true;
+			}
+		}
+		public override bool Paste(WidgetOperationArgs args, LiveObject after)
+		{
+			int index = -1;
+			if (after != null)
+			{
+				index = Tracks.IndexOf(after as LiveObject) + 1;
+			}
+			LiveObject clipboardData = Clipboards.Get<KeyframedWidget, LiveObject>();
+			if (clipboardData != null)
+			{
+				args.Timeline.CreateWidget(clipboardData.Copy(), index);
+				return true;
+			}
+			return false;
+		}
+
+		public override string ToString()
+		{
+			return GetLabel();
+		}
+
+		public string GetLabel()
+		{
+			return Name ?? "Unnamed scene";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveSprite.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveSprite.cs
index 43cbc47b7d3c7e9505ade517b25f0041b0951fd3..48d78b5c6d2ddbd4a27d810dcc0f2544dbcee3a8 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveSprite.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveSprite.cs	
@@ -1,1474 +1,233 @@
-using Desktop;
-using Desktop.CommonControls.PropertyControls;
-using Desktop.DataStructures;
-using SPNATI_Character_Editor.Controls;
-using SPNATI_Character_Editor.Controls.EditControls;
-using System;
+using System;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.ComponentModel;
 using System.Drawing;
 using System.Drawing.Drawing2D;
 using System.Drawing.Imaging;
 using System.Globalization;
-using System.Linq;
 
 namespace SPNATI_Character_Editor.EpilogueEditor
 {
-	public class LiveSprite : BindableObject, ILabel, IComparable<LiveSprite>
+	public class LiveSprite : LiveAnimatedObject
 	{
 		public SpriteWidget Widget;
-		public LivePose Pose;
-		public event EventHandler LabelChanged;
 
-		[Text(DisplayName = "Id", Key = "id", GroupOrder = 0)]
-		public string Id
+		#region Pose
+		public LiveSprite(LiveData data, float time) : this()
 		{
-			get { return Get<string>(); }
-			set
-			{
-				Set(value);
-				LabelChanged?.Invoke(this, EventArgs.Empty);
-			}
-		}
-
-		public LiveSprite Parent { get; private set; }
-		[Text(DisplayName = "ParentId", Key = "parent", GroupOrder = 2)]
-		public string ParentId
-		{
-			get { return Get<string>(); }
-			set
-			{
-				Set(value);
-				Parent = Pose.Sprites.Find(s => s.Id == value);
-				UpdateLocalTransform();
-			}
-		}
-
-		/// <summary>
-		/// Hide from canvas UI
-		/// </summary>
-		public bool Hidden
-		{
-			get { return Get<bool>(); }
-			set { Set(value); }
-		}
-
-		//Not exposing since there are still some questions about the best way to recognize this from a PoseDirective
-		//[Boolean(DisplayName = "Full Length", GroupOrder = 10, Description = "If checked, the sprite has no set end time.")]
-		public bool LinkedToEnd
-		{
-			get { return Get<bool>(); }
-			set { Set(value); }
-		}
-
-		[DirectiveMarker(DisplayName = "Marker", GroupOrder = 13, Key = "marker", Description = "Run this directive only if the marker's condition is met", ShowPrivate = true)]
-		public string Marker
-		{
-			get { return Get<string>(); }
-			set
-			{
-				bool perTarget;
-				MarkerOperator op;
-				string markerValue;
-				MarkerName = SPNATI_Character_Editor.Marker.ExtractConditionPieces(value, out op, out markerValue, out perTarget);
-				MarkerOp = op;
-				MarkerValue = markerValue;
-				Set(value);
-			}
-		}
-
-		private string MarkerName;
-		private MarkerOperator MarkerOp;
-		private string MarkerValue;
-
-		[Numeric(DisplayName = "Layer", Key = "z", GroupOrder = 15)]
-		public int Z
-		{
-			get { return Get<int>(); }
-			set { Set(value); }
-		}
-
-		[Float(DisplayName = "Pivot X", Key = "pivotx", GroupOrder = 20, Description = "X value of rotation/scale point of origin as a percentage of the sprite's physical size.", Minimum = -1000, Maximum = 1000, Increment = 0.1f)]
-		public float PivotX
-		{
-			get { return Get<float>(); }
-			set
-			{
-				if (float.IsNaN(value))
-				{
-					value = 0;
-				}
-				Set(value);
-				UpdateLocalTransform();
-			}
-		}
-		[Float(DisplayName = "Pivot Y", Key = "pivoty", GroupOrder = 20, Description = "Y value of Rotation/scale point of origin as a percentage of the sprite's physical size.", Minimum = -1000, Maximum = 1000, Increment = 0.1f)]
-		public float PivotY
-		{
-			get { return Get<float>(); }
-			set
-			{
-				if (float.IsNaN(value))
-				{
-					value = 0;
-				}
-				Set(value);
-				UpdateLocalTransform();
-			}
-		}
-
-		[Float(DisplayName = "Start", Key = "start", GroupOrder = 5, Description = "Starting time to display the sprite.", Minimum = 0, Maximum = 1000, Increment = 0.1f)]
-		public float Start
-		{
-			get { return Get<float>(); }
-			set { Set(value); }
-		}
-
-		//[AnimDuration(DisplayName = "Duration", GroupOrder = 8, Description = "Total time to display the sprite when not using Full Length.", Minimum = 0.1f, Maximum = 1000, Increment = 0.1f,
-		//			BoundProperties = new string[] { "LinkedToEnd", "Keyframes" })]
-		public float Length
-		{
-			get
-			{
-				if (Keyframes.Count > 1)
-				{
-					float time = Keyframes[Keyframes.Count - 1].Time;
-					return time;
-				}
-				return Get<float>();
-			}
-			set { Set(value); }
-		}
-
-		public ObservableCollection<string> Properties
-		{
-			get { return Get<ObservableCollection<string>>(); }
-			set { Set(value); }
-		}
-
-		public ObservableDictionary<string, AnimatedProperty> AnimatedProperties
-		{
-			get { return Get<ObservableDictionary<string, AnimatedProperty>>(); }
-			set { Set(value); }
-		}
-
-		public ObservableCollection<LiveKeyframe> Keyframes
-		{
-			get { return Get<ObservableCollection<LiveKeyframe>>(); }
-			set { Set(value); }
-		}
-
-		public bool IsVisible
-		{
-			get { return Time >= Start && (LinkedToEnd || Time <= Start + Length); }
-		}
-
-		public float Time { get; private set; }
-
-		public event EventHandler<LiveKeyframe> KeyframeChanged;
-
-		private float GetRelativeTime()
-		{
-			return Time - Start;
-		}
-
-		#region Interpolated values based on the current time
-		public Bitmap Image;
-		public int Height;
-		public int Width;
-
-		private float _x;
-		public float X
-		{
-			get { return _x; }
-			set { _x = (float)Math.Round(value, 0); UpdateLocalTransform(); }
-		}
-
-		private float _y;
-		public float Y
-		{
-			get { return _y; }
-			set { _y = (float)Math.Round(value, 0); UpdateLocalTransform(); }
-		}
-
-		private float _rotation;
-		public float Rotation
-		{
-			get { return _rotation; }
-			set { _rotation = (float)Math.Round(value, 2); UpdateLocalTransform(); }
-		}
-
-		private float _scaleX = 1;
-		public float ScaleX
-		{
-			get { return _scaleX; }
-			set
-			{
-				value = (float)Math.Round(value, 2);
-				if (value == 0)
-				{
-					value = 0.01f;
-				}
-				_scaleX = value;
-				UpdateLocalTransform();
-			}
-		}
-
-		private float _scaleY = 1;
-		public float ScaleY
-		{
-			get { return _scaleY; }
-			set
-			{
-				value = (float)Math.Round(value, 2);
-				if (value == 0)
-				{
-					value = 0.01f;
-				}
-				_scaleY = value;
-				UpdateLocalTransform();
-			}
-		}
-
-		private float _skewX = 0;
-		public float SkewX
-		{
-			get { return _skewX; }
-			set
-			{
-				value = (float)Math.Round(value, 2);
-				_skewX = value;
-			}
-		}
-
-		private float _skewY = 0;
-		public float SkewY
-		{
-			get { return _skewY; }
-			set
-			{
-				value = (float)Math.Round(value, 2);
-				_skewY = value;
-			}
-		}
-
-		public float Alpha = 100;
-
-		public Matrix LocalTransform { get; private set; }
-
-		private void UpdateLocalTransform()
-		{
-			Matrix transform = new Matrix();
-			float pivotX = PivotX * Width;
-			float pivotY = PivotY * Height;
-			transform.Translate(-pivotX, -pivotY, MatrixOrder.Append);
-			transform.Scale(ScaleX, ScaleY, MatrixOrder.Append);
-			transform.Rotate(Rotation, MatrixOrder.Append);
-			transform.Translate(pivotX, pivotY, MatrixOrder.Append);
-
-			transform.Translate(X - (Parent == null ? Width / 2 : 0), Y, MatrixOrder.Append); //local position
-			LocalTransform = transform;
-		}
-
-		public Matrix WorldTransform
-		{
-			get
-			{
-				LiveSprite transform = this;
-				Matrix m = new Matrix();
-				while (transform != null)
-				{
-					m.Multiply(transform.LocalTransform, MatrixOrder.Append);
-
-					if (transform.Parent == this)
-					{
-						transform = null;
-					}
-					else
-					{
-						transform = transform.Parent;
-					}
-				}
-				return m;
-			}
-		}
-
-		public Matrix UnscaledWorldTransform
-		{
-			get
-			{
-				LiveSprite transform = this;
-				Matrix m = new Matrix();
-				while (transform != null)
-				{
-					Matrix localTransform;
-					if (transform == this)
-					{
-						localTransform = new Matrix();
-						localTransform.Translate(X - (Parent == null ? Width / 2 : 0), Y, MatrixOrder.Append);
-					}
-					else
-					{
-						localTransform = transform.LocalTransform;
-					}
-
-					m.Multiply(localTransform, MatrixOrder.Append);
-
-					if (transform.Parent == this)
-					{
-						transform = null;
-					}
-					else
-					{
-						transform = transform.Parent;
-					}
-				}
-				return m;
-			}
-		}
-
-		/// <summary>
-		/// Converts a point from local space to screen space
-		/// </summary>
-		/// <param name="x"></param>
-		/// <param name="y"></param>
-		/// <returns></returns>
-		public PointF ToScreenPt(float x, float y, Matrix sceneTransform)
-		{
-			PointF[] pt = new PointF[] { new PointF(x, y) };
-			return ToScreenPt(sceneTransform, pt)[0];
-		}
-
-		/// <summary>
-		/// Converts a point from local space to screen space
-		/// </summary>
-		/// <param name="x"></param>
-		/// <param name="y"></param>
-		/// <returns></returns>
-		public PointF[] ToScreenPt(Matrix sceneTransform, params PointF[] pts)
-		{
-			Matrix m = new Matrix();
-			m.Multiply(sceneTransform);
-			m.Multiply(WorldTransform);
-			m.TransformPoints(pts);
-			return pts;
-		}
-
-		/// <summary>
-		/// Converts a point from screen spcae to local space
-		/// </summary>
-		/// <param name="x"></param>
-		/// <param name="y"></param>
-		/// <param name="sceneTransform"></param>
-		/// <returns></returns>
-		public PointF ToLocalPt(float x, float y, Matrix sceneTransform)
-		{
-			PointF[] pt = new PointF[] { new PointF(x, y) };
-			return ToLocalPt(sceneTransform, pt)[0];
-		}
-
-		/// <summary>
-		/// Converts one or more points in screen space to local space
-		/// </summary>
-		/// <param name="sceneTransform"></param>
-		/// <param name="pts"></param>
-		/// <returns></returns>
-		public PointF[] ToLocalPt(Matrix sceneTransform, params PointF[] pts)
-		{
-			Matrix m = new Matrix();
-			m.Multiply(sceneTransform);
-			m.Multiply(WorldTransform);
-			m.Invert();
-			m.TransformPoints(pts);
-			return pts;
-		}
-
-		/// <summary>
-		/// Converts a point in local space to world space
-		/// </summary>
-		/// <param name="x"></param>
-		/// <param name="y"></param>
-		/// <returns></returns>
-		public PointF ToWorldPt(float x, float y)
-		{
-			PointF[] pt = new PointF[] { new PointF(x, y) };
-			return ToWorldPt(pt)[0];
-		}
-		/// <summary>
-		/// Converts one or more points in local space to world space
-		/// </summary>
-		/// <param name="sceneTransform"></param>
-		/// <param name="pts"></param>
-		/// <returns></returns>
-		public PointF[] ToWorldPt(params PointF[] pts)
-		{
-			if (Parent == null)
-			{
-				return pts;
-			}
-			Matrix m = new Matrix();
-			m.Multiply(Parent.WorldTransform);
-			m.TransformPoints(pts);
-			return pts;
-		}
-
-		/// <summary>
-		/// Converts one or more points in screen space to world space
-		/// </summary>
-		/// <param name="sceneTransform"></param>
-		/// <param name="pts"></param>
-		/// <returns></returns>
-		public PointF[] ScreenToWorldPt(Matrix sceneTransform, params PointF[] pts)
-		{
-			Matrix m = new Matrix();
-			m.Multiply(sceneTransform);
-			m.Invert();
-			m.TransformPoints(pts);
-			return pts;
-		}
-
-		/// <summary>
-		/// Converts a point from world space to local space
-		/// </summary>
-		/// <param name="pt"></param>
-		/// <returns></returns>
-		public PointF WorldToLocalPt(PointF pt)
-		{
-			PointF[] pts = new PointF[] { pt };
-			Matrix m = new Matrix();
-			m.Multiply(WorldTransform);
-			m.Invert();
-			m.TransformPoints(pts);
-			return pts[0];
-		}
-
-		/// <summary>
-		/// Converts a point from screen space to local unscaled space
-		/// </summary>
-		/// <param name="x"></param>
-		/// <param name="y"></param>
-		/// <returns></returns>
-		public PointF[] ToLocalUnscaledPt(Matrix sceneTransform, params PointF[] pts)
-		{
-			Matrix m = new Matrix();
-			m.Multiply(sceneTransform);
-			m.Multiply(UnscaledWorldTransform);
-			m.Invert();
-			m.TransformPoints(pts);
-			return pts;
-		}
-
-		public float WorldAlpha
-		{
-			get
-			{
-				float alpha = 1;
-				LiveSprite parent = this;
-				while (parent != null)
-				{
-					alpha *= parent.Alpha / 100.0f;
-					parent = parent.Parent;
-				}
-				return alpha * 100;
-			}
-		}
-		#endregion
-
-		public LiveSprite(LivePose pose, float time) : this()
-		{
-			Pose = pose;
-			Length = 1;
-			Start = time;
-			Id = "New Sprite";
-			PivotX = 0.5f;
-			PivotY = 0.5f;
-			LinkedToEnd = true;
-			LiveKeyframe startFrame = new LiveKeyframe(0);
-			startFrame.X = 0;
-			startFrame.Y = 0;
-			AddKeyframe(startFrame);
-			Update(time, false);
-		}
-
-		public LiveSprite(LivePose pose, Sprite sprite, float time) : this()
-		{
-			Pose = pose;
-			ParentId = sprite.ParentId;
-			Marker = sprite.Marker;
-			Length = 0.5f;
-			Id = sprite.Id;
-			Z = sprite.Z;
-			Start = time;
-			LinkedToEnd = true;
-			if (!string.IsNullOrEmpty(sprite.Delay))
-			{
-				float start;
-				float.TryParse(sprite.Delay, NumberStyles.Number, CultureInfo.InvariantCulture, out start);
-				Start = start;
-				Length = 1;
-			}
-			if (!string.IsNullOrEmpty(sprite.PivotX))
-			{
-				float pivot;
-				string pivotX = sprite.PivotX;
-				if (pivotX.EndsWith("%"))
-				{
-					pivotX = pivotX.Substring(0, pivotX.Length - 1);
-				}
-				float.TryParse(pivotX, NumberStyles.Number, CultureInfo.InvariantCulture, out pivot);
-				pivot /= 100.0f;
-				PivotX = pivot;
-			}
-			else
-			{
-				PivotX = 0.5f;
-			}
-			if (!string.IsNullOrEmpty(sprite.PivotY))
-			{
-				float pivot;
-				string pivotY = sprite.PivotY;
-				if (pivotY.EndsWith("%"))
-				{
-					pivotY = pivotY.Substring(0, pivotY.Length - 1);
-				}
-				float.TryParse(pivotY, NumberStyles.Number, CultureInfo.InvariantCulture, out pivot);
-				pivot /= 100.0f;
-				PivotY = pivot;
-			}
-			else
-			{
-				PivotY = 0.5f;
-			}
-			LiveKeyframe temp;
-			AddKeyframe(sprite, 0, false, out temp);
-			Update(time, false);
-		}
-
-		public string GetLabel()
-		{
-			return $"Sprite Settings: {Id}";
-		}
-
-		private LiveSprite(LiveSprite source) : this()
-		{
-			source.CopyPropertiesInto(this);
-			foreach (LiveKeyframe kf in this.Keyframes)
-			{
-				kf.Sprite = this;
-				kf.PropertyChanged += Kf_PropertyChanged;
-			}
-			Parent = source.Parent;
-			UpdateLocalTransform();
-		}
-
-		private LiveSprite()
-		{
-			Properties = new ObservableCollection<string>();
-			Keyframes = new ObservableCollection<LiveKeyframe>();
-			AnimatedProperties = new ObservableDictionary<string, AnimatedProperty>();
-		}
-
-		public override string ToString()
-		{
-			return $"{Id} ({Start})";
-		}
-
-		public LiveSprite Copy()
-		{
-			return new LiveSprite(this);
-		}
-
-		public AnimatedProperty GetAnimationProperties(string propName)
-		{
-			AnimatedProperty props;
-			AnimatedProperties.TryGetValue(propName, out props);
-			return props ?? new AnimatedProperty(propName);
-		}
-
-		public void AddValue<T>(float time, string propName, string serializedValue)
-		{
-			AddValue<T>(time, propName, serializedValue, false);
-		}
-
-		/// <summary>
-		/// Adds a property value to a keyframe at the given time
-		/// </summary>
-		/// <param name="time">Time in seconds from start </param>
-		/// <param name="propName"></param>
-		/// <param name="serializedValue"></param>
-		/// <returns>Keyframe at that point</returns>
-		private void AddValue<T>(float time, string propName, string serializedValue, bool addAnimBreak)
-		{
-			if (string.IsNullOrEmpty(serializedValue))
-			{
-				return;
-			}
-			if (!AnimatedProperties.ContainsKey(propName))
-			{
-				AddAnimatedProperty(propName);
-			}
-			LiveKeyframe keyframe = Keyframes.Find(k => k.Time == time);
-			if (keyframe == null)
-			{
-				keyframe = AddKeyframe(time);
-			}
-
-			if (addAnimBreak)
-			{
-				keyframe.InterpolationBreaks[propName] = true;
-			}
-
-			object val = null;
-			Type propType = typeof(T);
-			if (propType == typeof(string))
-			{
-				val = serializedValue;
-			}
-			else if (propType == typeof(float))
-			{
-				float valFloat;
-				float.TryParse(serializedValue, NumberStyles.Number, CultureInfo.InvariantCulture, out valFloat);
-				val = valFloat;
-			}
-			else if (propType == typeof(int))
-			{
-				int valInt;
-				int.TryParse(serializedValue, out valInt);
-				val = valInt;
-			}
-			else
-			{
-				throw new ArgumentException($"Type {typeof(T).Name} not supported.");
-			}
-			keyframe.Set(val, propName);
-		}
-
-		/// <summary>
-		/// Merges a directive into this preview to have one single animation
-		/// </summary>
-		/// <param name="directive"></param>
-		public void AddDirective(PoseDirective directive)
-		{
-			float delay = Start;
-			if (!string.IsNullOrEmpty(directive.Delay))
-			{
-				float.TryParse(directive.Delay, NumberStyles.Number, CultureInfo.InvariantCulture, out delay);
-			}
-			float startTime = delay - Start;
-			if (startTime < 0)
-			{
-				startTime = 0; //if the delay was shorter than the sprite's delay, use no delay at all. This setup wouldn't work well anyway.
-			}
-
-			HashSet<string> affectedProperties = new HashSet<string>();
-			directive.Keyframes.Sort((k1, k2) =>
-			{
-				string t1 = k1.Time ?? "0";
-				string t2 = k2.Time ?? "0";
-				return t1.CompareTo(t2);
-			});
-			for (int i = 0; i < directive.Keyframes.Count; i++)
-			{
-				Keyframe kf = directive.Keyframes[i];
-				bool addBreak = (i == 0 && startTime > 0);
-				LiveKeyframe liveFrame;
-				HashSet<string> properties = AddKeyframe(kf, startTime, addBreak, out liveFrame);
-
-				foreach (string prop in properties)
-				{
-					affectedProperties.Add(prop);
-
-					//if (prop == "Alpha" && i == directive.Keyframes.Count - 1 && kf.Opacity == "0")
-					//{
-					//	//if setting alpha to 0 on the last frame, consider this to be the sprite's duration
-					//	liveFrame.Delete("Alpha");
-					//	LinkedToEnd = false;
-					//	Length = liveFrame.Time;
-					//}
-				}
-			}
-			foreach (string prop in affectedProperties)
-			{
-				AnimatedProperty animatedProperty = GetAnimationProperties(prop);
-				string ease = directive.EasingMethod;
-				string interpolation = directive.InterpolationMethod;
-				string currentEase = animatedProperty.Ease.GetValue(startTime);
-				string currentInterpolate = animatedProperty.Interpolation.GetValue(startTime);
-				if ((string.IsNullOrEmpty(currentEase) || ease != currentEase && ease != null) || (string.IsNullOrEmpty(currentInterpolate) || interpolation != currentInterpolate && interpolation != null))
-				{
-					animatedProperty.Ease.SetValue(startTime, ease);
-					animatedProperty.Interpolation.SetValue(startTime, interpolation);
-					if (startTime > 0)
-					{
-						LiveKeyframe frame = Keyframes.Find(k => k.Time == Start);
-						if (frame != null)
-						{
-							frame.InterpolationBreaks[prop] = true;
-						}
-					}
-				}
-				animatedProperty.Looped = animatedProperty.Looped || directive.Looped;
-			}
-		}
-
-		/// <summary>
-		/// Adds a keyframe to the LiveSprite
-		/// </summary>
-		/// <param name="kf"></param>
-		public HashSet<string> AddKeyframe(Keyframe kf, float timeOffset, bool addBreak, out LiveKeyframe frame)
-		{
-			HashSet<string> properties = new HashSet<string>();
-
-			float time;
-			float.TryParse(kf.Time, NumberStyles.Number, CultureInfo.InvariantCulture, out time);
-			time += timeOffset;
-
-			if (!string.IsNullOrEmpty(kf.Src))
-			{
-				AddValue<string>(time, "Src", kf.Src, addBreak);
-				properties.Add("Src");
-			}
-			if (!string.IsNullOrEmpty(kf.X))
-			{
-				AddValue<float>(time, "X", kf.X, addBreak);
-				properties.Add("X");
-			}
-			if (!string.IsNullOrEmpty(kf.Y))
-			{
-				AddValue<float>(time, "Y", kf.Y, addBreak);
-				properties.Add("Y");
-			}
-			if (!string.IsNullOrEmpty(kf.ScaleX))
-			{
-				AddValue<float>(time, "ScaleX", kf.ScaleX, addBreak);
-				properties.Add("ScaleX");
-			}
-			if (!string.IsNullOrEmpty(kf.ScaleY))
-			{
-				AddValue<float>(time, "ScaleY", kf.ScaleY, addBreak);
-				properties.Add("ScaleY");
-			}
-			if (!string.IsNullOrEmpty(kf.Opacity))
-			{
-				AddValue<float>(time, "Alpha", kf.Opacity, addBreak);
-				properties.Add("Alpha");
-			}
-			if (!string.IsNullOrEmpty(kf.Rotation))
-			{
-				AddValue<float>(time, "Rotation", kf.Rotation, addBreak);
-				properties.Add("Rotation");
-			}
-			if (!string.IsNullOrEmpty(kf.SkewX))
-			{
-				AddValue<float>(time, "SkewX", kf.SkewX, addBreak);
-				properties.Add("SkewX");
-			}
-			if (!string.IsNullOrEmpty(kf.SkewY))
-			{
-				AddValue<float>(time, "SkewX", kf.SkewY, addBreak);
-				properties.Add("SkewY");
-			}
-
-			frame = Keyframes.Find(k => k.Time == time);
-			return properties;
-		}
-
-		public LiveKeyframe AddKeyframe(float time)
-		{
-			LiveKeyframe kf = new LiveKeyframe(time);
-			AddKeyframe(kf);
-			return kf;
-		}
-
-		public void AddKeyframe(LiveKeyframe kf)
-		{
-			kf.Sprite = this;
-			kf.PropertyChanged += Kf_PropertyChanged;
-			Keyframes.Add(kf);
-
-			foreach (string prop in LiveKeyframe.TrackedProperties)
-			{
-				if (kf.HasProperty(prop))
-				{
-					UpdateProperty(prop);
-				}
-			}
-
-			ResortKeyframes();
-		}
-
-		public void RemoveKeyframe(LiveKeyframe kf)
-		{
-			kf.PropertyChanged -= Kf_PropertyChanged;
-			kf.Sprite = null;
-			Keyframes.Remove(kf);
-
-			foreach (string prop in LiveKeyframe.TrackedProperties)
-			{
-				if (kf.HasProperty(prop))
-				{
-					if (kf.Time > 0)
-					{
-						AnimatedProperty animatedProp = GetAnimationProperties(prop);
-						animatedProp.Ease.RemoveValue(kf.Time);
-						animatedProp.Interpolation.RemoveValue(kf.Time);
-					}
-					UpdateProperty(prop);
-				}
-			}
-		}
-
-		private void ResortKeyframes()
-		{
-			Keyframes.Sort((k1, k2) => k1.Time.CompareTo(k2.Time));
-		}
-
-		private void Kf_PropertyChanged(object sender, PropertyChangedEventArgs e)
-		{
-			LiveKeyframe frame = sender as LiveKeyframe;
-			if (e.PropertyName == "Time")
-			{
-				ResortKeyframes();
-			}
-			else if (e.PropertyName == "InterpolationBreaks")
-			{
-			}
-			else
-			{
-				if (!frame.HasProperty(e.PropertyName))
-				{
-					AnimatedProperty prop = GetAnimationProperties(e.PropertyName);
-					prop.Ease.RemoveValue(frame.Time);
-					prop.Interpolation.RemoveValue(frame.Time);
-				}
-				UpdateProperty(e.PropertyName);
-
-				//wipe out the frame if it has no properties remaining
-				if (frame.IsEmpty && frame.Time > 0)
-				{
-					RemoveKeyframe(frame);
-				}
-
-				KeyframeChanged?.Invoke(this, frame);
-			}
-		}
-
-		/// <summary>
-		/// Updates the Properties array when a property changes
-		/// </summary>
-		/// <param name="property"></param>
-		private void UpdateProperty(string property)
-		{
-			bool hasProperty = AnimatedProperties.ContainsKey(property);
-			int count = Keyframes.Count(kf => kf.HasProperty(property));
-			if (count == 0 && hasProperty)
-			{
-				//need to remove the property
-				RemoveAnimatedProperty(property);
-			}
-			else if (count > 0 && !hasProperty)
-			{
-				//need to add the property
-				AddAnimatedProperty(property);
-			}
-		}
-
-		private void AddAnimatedProperty(string property)
-		{
-			PropertyDefinition propertyDef = Definitions.Instance.Get<PropertyDefinition>(property);
-			bool inserted = false;
-			for (int i = 0; i < Properties.Count; i++)
-			{
-				string prop = Properties[i];
-				PropertyDefinition otherDef = Definitions.Instance.Get<PropertyDefinition>(prop);
-				int compare = propertyDef.CompareTo(otherDef);
-				if (compare < 0)
-				{
-					Properties.Insert(i, property);
-					inserted = true;
-					break;
-				}
-			}
-			if (!inserted)
-			{
-				Properties.Add(property);
-			}
-			AnimatedProperty anim = new AnimatedProperty(property);
-			anim.Interpolation.SetValue(0, property == "Src" ? null : "linear");
-			anim.Ease.SetValue(0, null);
-			AnimatedProperties[property] = anim;
-		}
-
-		private void RemoveAnimatedProperty(string property)
-		{
-			Properties.Remove(property);
-			AnimatedProperties.Remove(property);
-		}
-
-		/// <summary>
-		/// Gets the time a particular property is animating.
-		/// </summary>
-		/// <remarks>
-		/// If the property's 1st non-0 keyframe has the same value as time 0, then that frame will be treated as the starting frame
-		/// </remarks>
-		/// <param name="property"></param>
-		/// <returns></returns>
-		private float GetPropertyDuration(string property, float time, out float start, out float end)
-		{
-			start = 0;
-			end = 0;
-			List<LiveKeyframe> validFrames = new List<LiveKeyframe>();
-			for (int i = 0; i < Keyframes.Count; i++)
-			{
-				LiveKeyframe kf = Keyframes[i];
-				if (kf.HasProperty(property))
-				{
-					if (kf.Time <= time && kf.InterpolationBreaks.ContainsKey(property))
-					{
-						validFrames.Clear();
-					}
-					else if (kf.Time > time && kf.InterpolationBreaks.ContainsKey(property))
-					{
-						break;
-					}
-
-					validFrames.Add(kf);
-				}
-			}
-
-			if (validFrames.Count == 0)
-			{
-				return 0;
-			}
-
-			start = validFrames[0].Time;
-			end = validFrames[validFrames.Count - 1].Time;
-			return end - start;
+			Data = data;
+			Length = 1;
+			Start = time;
+			Id = "New Sprite";
+			PivotX = 0.5f;
+			PivotY = 0.5f;
+			LiveKeyframe startFrame = CreateKeyframe(0);
+			startFrame.X = 0;
+			startFrame.Y = 0;
+			AddKeyframe(startFrame);
+			Update(time, 0, false);
 		}
 
-		/// <summary>
-		/// Gets the value of a property at the given point in time
-		/// </summary>
-		/// <typeparam name="T">Property type</typeparam>
-		/// <param name="property">Property name</param>
-		/// <param name="time">Time in seconds from the start of the anim</param>
-		/// <param name="defaultValue">Value to use if no frames define this property</param>
-		/// <returns>Interpolated value at the given point in time</returns>
-		public T GetPropertyValue<T>(string property, float time, T defaultValue)
-		{
-			return GetPropertyValue<T>(property, time, defaultValue, null, null, null);
-		}
-		/// <summary>
-		/// Gets the value of a property at the given point in time
-		/// </summary>
-		/// <typeparam name="T">Property type</typeparam>
-		/// <param name="property">Property name</param>
-		/// <param name="time">Time in seconds from the start of the anim</param>
-		/// <param name="defaultValue">Value to use if no frames define this property</param>
-		/// <param name="easeOverride">Ease to use instead of the property's defined ease</param>
-		/// <param name="interpolationOverride">Interpolation to use instead of the property's defined interpolation</param>
-		/// <returns>Interpolated value at the given point in time</returns>
-		public T GetPropertyValue<T>(string property, float time, T defaultValue, string easeOverride, string interpolationOverride, bool? loopOverride)
+		public LiveSprite(LivePose pose, Sprite sprite, float time) : this()
 		{
-			float start;
-			float end;
-			float t = GetInterpolatedTime(property, time, easeOverride, loopOverride, out start, out end);
-			t = start + t * (end - start);
-
-			Type parentType = typeof(LiveKeyframe);
-
-			AnimatedProperty propertyAnimation = GetAnimationProperties(property);
-			string frameInterp = propertyAnimation.Interpolation.GetValue(t);
-			string interpolation = interpolationOverride ?? frameInterp;
-			if (string.IsNullOrEmpty(frameInterp) || frameInterp == "none")
+			Data = pose;
+			ParentId = sprite.ParentId;
+			Marker = sprite.Marker;
+			Length = 0.5f;
+			Id = sprite.Id;
+			Z = sprite.Z;
+			Start = time;
+			if (!string.IsNullOrEmpty(sprite.Delay))
 			{
-				interpolation = "none";
+				float start;
+				float.TryParse(sprite.Delay, NumberStyles.Number, CultureInfo.InvariantCulture, out start);
+				Start = start;
+				Length = 1;
 			}
-
-			LiveKeyframe previousFrame = null;
-			LiveKeyframe previousPreviousFrame = null;
-			LiveKeyframe nextFrame = null;
-			LiveKeyframe nextNextFrame = null;
-			bool foundNext = false;
-			bool foundNextNext = false;
-			Stack<LiveKeyframe> validFrames = new Stack<LiveKeyframe>();
-
-			for (int i = 0; i < Keyframes.Count; i++)
+			if (!string.IsNullOrEmpty(sprite.PivotX))
 			{
-				LiveKeyframe kf = Keyframes[i];
-				if (!kf.HasProperty(property))
-				{
-					continue;
-				}
-				if (kf.Time <= t && kf.InterpolationBreaks.ContainsKey(property))
-				{
-					foundNext = false;
-					foundNextNext = false;
-					validFrames.Clear();
-				}
-				if (kf.Time > t && kf.InterpolationBreaks.ContainsKey(property))
-				{
-					break;
-				}
-				validFrames.Push(kf);
-				if (kf.Time > t)
+				float pivot;
+				string pivotX = sprite.PivotX;
+				if (pivotX.EndsWith("%"))
 				{
-					if (foundNext)
-					{
-						foundNextNext = true;
-						break;
-					}
-					foundNext = true;
+					pivotX = pivotX.Substring(0, pivotX.Length - 1);
 				}
+				float.TryParse(pivotX, NumberStyles.Number, CultureInfo.InvariantCulture, out pivot);
+				pivot /= 100.0f;
+				PivotX = pivot;
 			}
-
-			if (foundNextNext && validFrames.Count > 0)
-			{
-				nextNextFrame = validFrames.Pop();
-			}
-			if (validFrames.Count > 0)
-			{
-				nextFrame = validFrames.Pop();
-			}
-			if (validFrames.Count > 0)
-			{
-				previousFrame = validFrames.Pop();
-			}
-			if (validFrames.Count > 0)
-			{
-				previousPreviousFrame = validFrames.Pop();
-			}
-
-			if (nextFrame != null)
-			{
-				previousFrame = previousFrame ?? nextFrame;
-				nextNextFrame = nextNextFrame ?? nextFrame;
-				previousPreviousFrame = previousPreviousFrame ?? previousFrame;
-				object previous = previousFrame.Get<object>(property);
-				object next = nextFrame.Get<object>(property);
-				object previousPrevious = previousPreviousFrame.Get<object>(property);
-				object nextNext = nextNextFrame.Get<object>(property);
-				float prevTime = previousFrame.Time;
-				float nextTime = nextFrame.Time;
-				float frameT = nextTime == prevTime ? 0 : (t - prevTime) / (nextTime - prevTime);
-				Type propertyType = PropertyTypeInfo.GetType(parentType, property);
-				return (T)AnimationHelpers.Interpolate(propertyType, previous, next, interpolation, frameT, previousPrevious, nextNext);
-			}
-			return defaultValue;
-		}
-
-		/// <summary>
-		/// Gets a time from 0-1 where 0=first frame and 1=last frame based on a property's keyframes and animation settings
-		/// </summary>
-		/// <param name="property"></param>
-		/// <param name="time"></param>
-		/// <param name="easeOverride"></param>
-		/// <param name="interpolationOverride"></param>
-		/// <param name="start"></param>
-		/// <returns></returns>
-		public float GetInterpolatedTime(string property, float time, string easeOverride, bool? loopOverride, out float start, out float end)
-		{
-			time -= Start; //use relative time
-			time = Math.Max(0, time);
-
-			//figure out this property's duration, which is from the first frame past time 0 if that frame has the same value as time 0, otherwise from time 0, to the last frame modifying this property
-			start = 0;
-			end = 0;
-			float duration = GetPropertyDuration(property, time, out start, out end);
-
-			AnimatedProperty propertyAnimation = GetAnimationProperties(property);
-			string ease = easeOverride ?? propertyAnimation.Ease.GetValue(time);
-			bool looped = loopOverride.HasValue ? loopOverride.Value : propertyAnimation.Looped;
-
-			if (time < start)
+			else
 			{
-				return 0;
+				PivotX = 0.5f;
 			}
-			else if (time > end)
+			if (!string.IsNullOrEmpty(sprite.PivotY))
 			{
-				if (looped)
-				{
-					while (duration > 0.0001f && time > end)
-					{
-						time -= duration;
-					}
-				}
-				else
+				float pivot;
+				string pivotY = sprite.PivotY;
+				if (pivotY.EndsWith("%"))
 				{
-					return 1;
+					pivotY = pivotY.Substring(0, pivotY.Length - 1);
 				}
+				float.TryParse(pivotY, NumberStyles.Number, CultureInfo.InvariantCulture, out pivot);
+				pivot /= 100.0f;
+				PivotY = pivot;
 			}
-
-			float relativeTime = 0;
-			if (duration > 0)
+			else
 			{
-				relativeTime = (time - start) / duration;
+				PivotY = 0.5f;
 			}
-
-			float t = AnimationHelpers.Ease(ease, relativeTime);
-			return t;
+			LiveKeyframe temp;
+			AddKeyframe(sprite, 0, false, out temp);
+			Update(time, 0, false);
 		}
+		#endregion
 
-		/// <summary>
-		/// Moves one or more properties from one keyframe to another (generating a new frame if it needs to)
-		/// </summary>
-		/// <param name="sourceFrame">Keyframe that the property originated on</param>
-		/// <param name="time">Relative time to move the property to</param>
-		/// <param name="targetFrame">Frame to move to. If not provided, a new frame at time will be generated</param>
-		/// <returns>Keyframe containing the property after moving it</returns>
-		public LiveKeyframe MoveProperty(LiveKeyframe sourceFrame, List<string> properties, float time, LiveKeyframe targetFrame)
+		#region Epilogue
+		public LiveSprite(LiveScene scene, Directive directive, Character character, float time) : this()
 		{
-			if (targetFrame != null && !Keyframes.Contains(targetFrame))
+			CenterX = false;
+			DisplayPastEnd = false;
+			Data = scene;
+			ParentId = directive.ParentId;
+			Length = 0.5f;
+			Id = directive.Id;
+			Z = directive.Layer;
+			Start = time;
+			if (!string.IsNullOrEmpty(directive.Delay))
 			{
-				AddKeyframe(targetFrame);
+				float start;
+				float.TryParse(directive.Delay, NumberStyles.Number, CultureInfo.InvariantCulture, out start);
+				Start += start;
+				Length = 1;
 			}
-			targetFrame = targetFrame ?? Keyframes.Find(k => k.Time == time);
-			foreach (string property in properties)
+			if (!string.IsNullOrEmpty(directive.PivotX))
 			{
-				if (!sourceFrame.HasProperty(property))
+				float pivot;
+				string pivotX = directive.PivotX;
+				if (pivotX.EndsWith("%"))
 				{
-					throw new ArgumentException($"Cannot move a property that doesn't exist: {property}.", nameof(properties));
+					pivotX = pivotX.Substring(0, pivotX.Length - 1);
 				}
-			}
-			if (targetFrame == sourceFrame)
-			{
-				//if moving onto the same keyframe, just update the time
-				targetFrame.Time = time;
+				float.TryParse(pivotX, NumberStyles.Number, CultureInfo.InvariantCulture, out pivot);
+				pivot /= 100.0f;
+				PivotX = pivot;
 			}
 			else
 			{
-				//if the affected properties are the only properties on the sourceFrame, and there is no targetFrame, then just move the whole frame
-				if (targetFrame == null && sourceFrame.PropertyCount == properties.Count)
-				{
-					sourceFrame.Time = time;
-					targetFrame = sourceFrame;
-				}
-				else
-				{
-					foreach (string property in properties)
-					{
-						object val = sourceFrame.Get<object>(property);
-
-						//1. Remove it from the previous keyframe, which might delete the sourceKeyframe too
-						sourceFrame.Delete(property);
-
-						//2. Create a new keyframe if needed
-						if (targetFrame == null)
-						{
-							targetFrame = AddKeyframe(time);
-						}
-
-						//3. Put the property into the target frame
-						targetFrame.Set(val, property);
-					}
-				}
-			}
-
-			return targetFrame;
-		}
-
-		/// <summary>
-		/// Copies one or more properties from a keyframe into a new, loose keyframe
-		/// </summary>
-		/// <param name="keyframe">Keyframe to copy</param>
-		/// <param name="properties">Properties to copy</param>
-		/// <returns></returns>
-		public LiveKeyframe CopyKeyframe(LiveKeyframe keyframe, HashSet<string> properties)
-		{
-			LiveKeyframe copy = new LiveKeyframe(keyframe.Time);
-			if (properties.Count == 0)
-			{
-				keyframe.CopyPropertiesInto(copy);
-				return copy;
+				PivotX = 0.5f;
 			}
-			else
+			if (!string.IsNullOrEmpty(directive.PivotY))
 			{
-				foreach (string property in properties)
+				float pivot;
+				string pivotY = directive.PivotY;
+				if (pivotY.EndsWith("%"))
 				{
-					copy.Set(keyframe.Get<object>(property), property);
-					if (keyframe.InterpolationBreaks.ContainsKey(property))
-					{
-						copy.InterpolationBreaks[property] = true;
-					}
+					pivotY = pivotY.Substring(0, pivotY.Length - 1);
 				}
-				return copy;
+				float.TryParse(pivotY, NumberStyles.Number, CultureInfo.InvariantCulture, out pivot);
+				pivot /= 100.0f;
+				PivotY = pivot;
 			}
-		}
-
-		/// <summary>
-		/// Copies the properties from a keyframe into this sprite, replacing any previous properties at that time
-		/// </summary>
-		/// <param name="source">Keyframe to copy from</param>
-		/// <param name="time">Time to paste properties at</param>
-		/// <param name="target">Target frame to paste to. If not provided, a new frame will be created.</param>
-		/// <returns></returns>
-		public LiveKeyframe PasteKeyframe(LiveKeyframe source, float time, LiveKeyframe target)
-		{
-			target = target ?? Keyframes.Find(kf => kf.Time == time);
-			if (target == null)
+			else
 			{
-				target = AddKeyframe(time);
+				PivotY = 0.5f;
 			}
-			else if (!Keyframes.Contains(target))
+			LiveKeyframe temp;
+			string oldSrc = directive.Src;
+			if (!string.IsNullOrEmpty(directive.Src))
 			{
-				AddKeyframe(target);
+				directive.Src = character.FolderName + "/" + directive.Src;
 			}
-			source.CopyPropertiesInto(target);
-			target.Time = time;
-			return target;
+			AddKeyframe(directive, 0, false, out temp);
+			directive.Src = oldSrc;
+			Update(time, 0, false);
 		}
+		#endregion
 
-		/// <summary>
-		/// Creates a keyframe representing the interpolated values at a particular time, without adding the frame to the sprite
-		/// </summary>
-		/// <param name="time">Relative time</param>
-		/// <returns></returns>
-		public LiveKeyframe GetInterpolatedFrame(float time)
+		public LiveSprite() : base()
 		{
-			LiveKeyframe frame = new LiveKeyframe(time);
-
-			foreach (string property in Properties)
-			{
-				frame.Set(GetPropertyValue<object>(property, time, null), property);
-			}
-
-			return frame;
-		}
 
-		#region Point-and-click editing
-		/// <summary>
-		/// Sets the object's local position so that its world position is at the given value
-		/// </summary>
-		/// <param name="x"></param>
-		/// <param name="y"></param>
-		public void SetWorldPosition(PointF worldPos)
-		{
-			PointF local = Parent == null ? worldPos : Parent.WorldToLocalPt(worldPos);
-			Translate(local.X, local.Y);
 		}
 
-		/// <summary>
-		/// Updates the sprite's position to a new value, updating the underlying data structures too
-		/// </summary>
-		/// <returns>List of objects that were modified</returns>
-		public void Translate(float x, float y)
+		public override string GetLabel()
 		{
-			if (X == x && Y == y)
-			{
-				return;
-			}
-
-			x = (float)Math.Round(x, 0);
-			y = (float)Math.Round(y, 0);
-
-			float time = GetRelativeTime();
-			AddValue<float>(time, "X", x.ToString(CultureInfo.InvariantCulture));
-			AddValue<float>(time, "Y", y.ToString(CultureInfo.InvariantCulture));
+			return $"Sprite Settings: {Id}";
 		}
 
-		public void AdjustPivot(PointF screenPt, Matrix sceneTransform)
+		public override Type GetKeyframeType()
 		{
-			PointF[] pts = new PointF[] {
-				screenPt
-			};
-			PointF[] localPts = ToLocalUnscaledPt(sceneTransform, pts);
-			PointF localPt = localPts[0];
-			float xPct = localPt.X / Width;
-			float yPct = localPt.Y / Height;
-
-			float pivotX = (float)Math.Round(xPct, 2);
-			float pivotY = (float)Math.Round(yPct, 2);
-			if (pivotX == PivotX && pivotY == PivotY)
-			{
-				return;
-			}
-			PivotX = xPct;
-			PivotY = yPct;
+			return typeof(LiveSpriteKeyframe);
 		}
 
-		public void Scale(Point screenPoint, Matrix sceneTransform, HoverContext context)
+		protected override void ParseKeyframe(Keyframe kf, bool addBreak, HashSet<string> properties, float time)
 		{
-			float time = GetRelativeTime();
-			bool horizontal = (context & HoverContext.ScaleHorizontal) != 0;
-			bool vertical = (context & HoverContext.ScaleVertical) != 0;
-
-			//scale is determined by first converting point to local space
-			PointF localPt = ToLocalPt(sceneTransform, screenPoint)[0];
-			PointF pivotPt = new PointF(PivotX * Width, PivotY * Height);
-
-			float scaleX = ScaleX;
-			float scaleY = ScaleY;
-
-			if (context.HasFlag(HoverContext.ScaleRight))
-			{
-				float scaledDist = Width - pivotPt.X;
-				float distFromPivot = localPt.X - pivotPt.X;
-				float unscaledDist = scaledDist / ScaleX;
-				scaleX = distFromPivot / unscaledDist;
-			}
-			else if (context.HasFlag(HoverContext.ScaleLeft))
+			if (!string.IsNullOrEmpty(kf.X))
 			{
-				float scaledDist = pivotPt.X;
-				float distFromPivot = pivotPt.X - localPt.X;
-				float unscaledDist = scaledDist / ScaleX;
-				scaleX = distFromPivot / unscaledDist;
+				AddValue<float>(time, "X", kf.X, addBreak);
+				properties.Add("X");
 			}
-			if (context.HasFlag(HoverContext.ScaleBottom))
+			if (!string.IsNullOrEmpty(kf.Y))
 			{
-				float scaledDist = Height - pivotPt.Y;
-				float distFromPivot = localPt.Y - pivotPt.Y;
-				float unscaledDist = scaledDist / ScaleY;
-				scaleY = distFromPivot / unscaledDist;
+				AddValue<float>(time, "Y", kf.Y, addBreak);
+				properties.Add("Y");
 			}
-			else if (context.HasFlag(HoverContext.ScaleTop))
+			if (!string.IsNullOrEmpty(kf.Src))
 			{
-				float scaledDist = pivotPt.Y;
-				float distFromPivot = pivotPt.Y - localPt.Y;
-				float unscaledDist = scaledDist / ScaleY;
-				scaleY = distFromPivot / unscaledDist;
+				AddValue<string>(time, "Src", kf.Src, addBreak);
+				properties.Add("Src");
 			}
-			if (horizontal && !float.IsInfinity(scaleX))
+			if (!string.IsNullOrEmpty(kf.ScaleX))
 			{
-				if (scaleX == 0)
-				{
-					scaleX = 0.01f;
-				}
-				scaleX = (float)Math.Round(scaleX, 2);
-				if (scaleX != ScaleX)
-				{
-					AddValue<float>(time, "ScaleX", scaleX.ToString(CultureInfo.InvariantCulture));
-				}
+				AddValue<float>(time, "ScaleX", kf.ScaleX, addBreak);
+				properties.Add("ScaleX");
 			}
-			if (vertical && !float.IsInfinity(scaleY))
+			if (!string.IsNullOrEmpty(kf.ScaleY))
 			{
-				if (scaleY == 0)
-				{
-					scaleY = 0.01f;
-				}
-				scaleY = (float)Math.Round(scaleY, 2);
-				if (scaleY != ScaleY)
-				{
-					AddValue<float>(time, "ScaleY", scaleY.ToString(CultureInfo.InvariantCulture));
-				}
+				AddValue<float>(time, "ScaleY", kf.ScaleY, addBreak);
+				properties.Add("ScaleY");
 			}
-		}
-
-		public void Rotate(Point screenPoint, PointF screenPivot, Point downPoint, float initialRotation)
-		{
-			//quick and dirty - just use the angle to look from the point to the center
-
-			double downAngle = Math.Atan2(screenPivot.Y - downPoint.Y, screenPivot.X - downPoint.X);
-			downAngle = downAngle * (180 / Math.PI) - 90;
-
-			double angle = Math.Atan2(screenPivot.Y - screenPoint.Y, screenPivot.X - screenPoint.X);
-			angle = angle * (180 / Math.PI) - 90;
-
-			angle -= downAngle;
-			double rotation = Math.Round(initialRotation + angle, 0);
-
-			if (Rotation == rotation)
+			if (!string.IsNullOrEmpty(kf.Opacity))
 			{
-				return;
+				AddValue<float>(time, "Alpha", kf.Opacity, addBreak);
+				properties.Add("Alpha");
 			}
-
-			float time = GetRelativeTime();
-			Rotation = (float)rotation;
-			AddValue<float>(time, "Rotation", Rotation.ToString(CultureInfo.InvariantCulture));
-		}
-
-		public void Skew(Point screenPoint, Point downPoint, HoverContext context, float zoom)
-		{
-			float dx = (screenPoint.X - downPoint.X) / zoom;
-			float dy = (screenPoint.Y - downPoint.Y) / zoom;
-			switch (context)
+			if (!string.IsNullOrEmpty(kf.Rotation))
 			{
-				case HoverContext.SkewLeft:
-					dy = -dy;
-					break;
-				case HoverContext.SkewRight:
-					break;
-				case HoverContext.SkewTop:
-					dx = -dx;
-					break;
+				AddValue<float>(time, "Rotation", kf.Rotation, addBreak);
+				properties.Add("Rotation");
 			}
-
-			float time = GetRelativeTime();
-
-			//skew formula: shift = size * tan(radians) / 2
-			//solved for angle: angle = atan(2 * shift / size)
-			if (HoverContext.SkewHorizontal.HasFlag(context))
+			if (!string.IsNullOrEmpty(kf.SkewX))
 			{
-				//skewX
-				float skewX = (float)(Math.Atan(2 * dx / Height) * 180 / Math.PI);
-				AddValue<float>(time, "SkewX", skewX.ToString(CultureInfo.InvariantCulture));
+				AddValue<float>(time, "SkewX", kf.SkewX, addBreak);
+				properties.Add("SkewX");
 			}
-			else
+			if (!string.IsNullOrEmpty(kf.SkewY))
 			{
-				//skewY
-				float skewY = (float)(Math.Atan(2 * dy / Width) * 180 / Math.PI);
-				AddValue<float>(time, "SkewY", skewY.ToString(CultureInfo.InvariantCulture));
+				AddValue<float>(time, "SkewX", kf.SkewY, addBreak);
+				properties.Add("SkewY");
 			}
 		}
-		#endregion
 
-		#region Drawing
-		public void Update(float time, bool inPlayback)
+		protected override void OnUpdate(float time, float offset, string easeOverride, string interpolationOverride, bool? looped, bool inPlayback)
 		{
-			Time = time;
-
-			string easeOverride = (inPlayback ? null : "linear");
-			string interpolationOverride = (inPlayback ? null : "linear");
-			bool? looped = (inPlayback ? null : new bool?(false));
-
-			string src = GetPropertyValue<string>("Src", time, null, easeOverride, interpolationOverride, looped);
+			X = GetPropertyValue("X", time, offset, 0.0f, easeOverride, interpolationOverride, looped);
+			Y = GetPropertyValue("Y", time, offset, 0.0f, easeOverride, interpolationOverride, looped);
+			string src = GetPropertyValue<string>("Src", time, 0, null, easeOverride, interpolationOverride, looped);
+			Src = src;
 			Image = LiveImageCache.Get(src);
-			if (Image != null)
-			{
-				Width = Image.Width;
-				Height = Image.Height;
-			}
-			else
-			{
-				Width = 100;
-				Height = 100;
-			}
-			X = GetPropertyValue("X", time, 0.0f, easeOverride, interpolationOverride, looped);
-			Y = GetPropertyValue("Y", time, 0.0f, easeOverride, interpolationOverride, looped);
-			ScaleX = GetPropertyValue("ScaleX", time, 1.0f, easeOverride, interpolationOverride, looped);
-			ScaleY = GetPropertyValue("ScaleY", time, 1.0f, easeOverride, interpolationOverride, looped);
-			Alpha = GetPropertyValue("Alpha", time, 100.0f, easeOverride, interpolationOverride, looped);
-			Rotation = GetPropertyValue("Rotation", time, 0.0f, easeOverride, interpolationOverride, looped);
-			SkewX = GetPropertyValue("SkewX", time, 0f, easeOverride, interpolationOverride, looped);
-			SkewY = GetPropertyValue("SkewY", time, 0f, easeOverride, interpolationOverride, looped);
+			ScaleX = GetPropertyValue("ScaleX", time, offset, 1.0f, easeOverride, interpolationOverride, looped);
+			ScaleY = GetPropertyValue("ScaleY", time, offset, 1.0f, easeOverride, interpolationOverride, looped);
+			Alpha = GetPropertyValue("Alpha", time, offset, 100.0f, easeOverride, interpolationOverride, looped);
+			Rotation = GetPropertyValue("Rotation", time, offset, 0.0f, easeOverride, interpolationOverride, looped);
+			SkewX = GetPropertyValue("SkewX", time, offset, 0f, easeOverride, interpolationOverride, looped);
+			SkewY = GetPropertyValue("SkewY", time, offset, 0f, easeOverride, interpolationOverride, looped);
 		}
 
-		public bool HiddenByMarker(List<string> markers)
+		public override ITimelineWidget CreateWidget(Timeline timeline)
 		{
-			if (markers != null && !string.IsNullOrEmpty(MarkerName))
-			{
-				switch (MarkerOp)
-				{
-					case MarkerOperator.NotEqual:
-					case MarkerOperator.LessThan:
-					case MarkerOperator.GreaterThan:
-						if (markers.Contains(MarkerName) && MarkerValue != "0" || !markers.Contains(MarkerName) && MarkerValue == "0")
-						{
-							return true;
-						}
-						break;
-					default:
-						if (markers.Contains(MarkerName) && MarkerValue == "0" || !markers.Contains(MarkerName) && MarkerValue != "0")
-						{
-							return true;
-						}
-						break;
-				}
-			}
-			return false;
+			return new SpriteWidget(this, timeline);
 		}
 
-		public void Draw(Graphics g, Matrix sceneTransform, List<string> markers)
+		public override void Draw(Graphics g, Matrix sceneTransform, List<string> markers, bool inPlayback)
 		{
 			if (!IsVisible || Hidden) { return; }
 			if (HiddenByMarker(markers))
@@ -1517,28 +276,5 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				g.ResetTransform();
 			}
 		}
-
-		/// <summary>
-		/// Gets the bounding box for an object centered at its screen-space origin using its true width+height as its size with no rotation
-		/// </summary>
-		/// <param name="sprite"></param>
-		/// <returns></returns>
-		public RectangleF GetUnscaledScreenSpaceBoundingBox(Matrix sceneTransform, float displayHeight, float zoom)
-		{
-			PointF spriteCenter = ToScreenPt(Width / 2, Height / 2, sceneTransform);
-			float displayScale = displayHeight / Pose.BaseHeight * zoom;
-			PointF scale = new PointF(displayScale, displayScale);
-			float width = Math.Abs((int)Math.Round(Width * scale.X));
-			float height = Math.Abs((int)Math.Round(Height * scale.Y));
-			float left = spriteCenter.X - width / 2;
-			float top = spriteCenter.Y - height / 2;
-			return new RectangleF(left, top, width, height);
-		}
-
-		public int CompareTo(LiveSprite other)
-		{
-			return Id.CompareTo(other.Id);
-		}
-		#endregion
 	}
 }
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveSpriteKeyframe.cs b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveSpriteKeyframe.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6e474963904f5385d3c372b364ca94171a1dc1f4
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/DataStructures/LiveSpriteKeyframe.cs	
@@ -0,0 +1,69 @@
+using Desktop.CommonControls.PropertyControls;
+using SPNATI_Character_Editor.Controls;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class LiveSpriteKeyframe : LiveKeyframe
+	{
+		public LiveSpriteKeyframe() : base()
+		{
+			TrackedProperties.Add("Src");
+			TrackedProperties.Add("ScaleX");
+			TrackedProperties.Add("ScaleY");
+			TrackedProperties.Add("Alpha");
+			TrackedProperties.Add("Rotation");
+			TrackedProperties.Add("SkewX");
+			TrackedProperties.Add("SkewY");
+		}
+
+		[FileSelect(DisplayName = "Source", GroupOrder = 10, Key = "src", Description = "Sprite source image")]
+		public string Src
+		{
+			get { return Get<string>(); }
+			set { Set(value); }
+		}
+
+		[Float(DisplayName = "Scale X", GroupOrder = 40, Key = "scalex", Increment = 0.1f, Minimum = -1000, Maximum = 1000)]
+		public float? ScaleX
+		{
+			get { return Get<float?>(); }
+			set { Set(value); }
+		}
+
+		[Float(DisplayName = "Scale Y", GroupOrder = 45, Key = "scaley", Increment = 0.1f, Minimum = -1000, Maximum = 1000)]
+		public float? ScaleY
+		{
+			get { return Get<float?>(); }
+			set { Set(value); }
+		}
+
+		[Slider(DisplayName = "Opacity (0-100)", GroupOrder = 30, Key = "alpha", Description = "Opacity/transparency level")]
+		public float? Alpha
+		{
+			get { return Get<float?>(); }
+			set { Set(value); }
+		}
+
+		[Float(DisplayName = "Rotation (deg)", GroupOrder = 50, Key = "rotation", Description = "Sprite rotation", DecimalPlaces = 0, Minimum = -7020, Maximum = 7020)]
+		public float? Rotation
+		{
+			get { return Get<float?>(); }
+			set { Set(value); }
+		}
+
+		[Float(DisplayName = "Skew X", GroupOrder = 60, Key = "skewx", Description = "Sprite shearing factor horizontally", DecimalPlaces = 2, Minimum = -89, Maximum = 89, Increment = 1f)]
+		public float? SkewX
+		{
+			get { return Get<float?>(); }
+			set { Set(value); }
+		}
+
+		[Float(DisplayName = "Skew Y", GroupOrder = 65, Key = "skewx", Description = "Sprite shearing factor vertically", DecimalPlaces = 2, Minimum = -89, Maximum = 89, Increment = 1f)]
+		public float? SkewY
+		{
+			get { return Get<float?>(); }
+			set { Set(value); }
+		}
+
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.Designer.cs b/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.Designer.cs
index 7750262dbb7f1892ea60b1dd1e52cb6477484974..85150f8f46a34b6ed695b8d02a1914900c45c524 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.Designer.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.Designer.cs	
@@ -30,9 +30,8 @@
 		{
 			this.components = new System.ComponentModel.Container();
 			System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(EpilogueCanvas));
-			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+			this.splitContainer1 = new Desktop.Skinning.SkinnedSplitContainer();
 			this.splitContainer2 = new System.Windows.Forms.SplitContainer();
-			this.treeScenes = new SPNATI_Character_Editor.Controls.SceneTree();
 			this.propertyTable = new Desktop.CommonControls.PropertyTable();
 			this.canvasStrip = new System.Windows.Forms.ToolStrip();
 			this.cmdLock = new System.Windows.Forms.ToolStripButton();
@@ -44,11 +43,13 @@
 			this.cmdPlayDirective = new System.Windows.Forms.ToolStripButton();
 			this.cmdPlay = new System.Windows.Forms.ToolStripButton();
 			this.canvas = new Desktop.CommonControls.SelectablePanel();
-			this.label1 = new System.Windows.Forms.Label();
-			this.lblZoom = new System.Windows.Forms.Label();
-			this.sliderZoom = new System.Windows.Forms.TrackBar();
-			this.lblCoord = new System.Windows.Forms.Label();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.lblZoom = new Desktop.Skinning.SkinnedLabel();
+			this.sliderZoom = new Desktop.Skinning.SkinnedSlider();
+			this.lblCoord = new Desktop.Skinning.SkinnedLabel();
 			this.tmrPlay = new System.Windows.Forms.Timer(this.components);
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.treeScenes = new SPNATI_Character_Editor.Controls.SceneTree();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
 			this.splitContainer1.Panel1.SuspendLayout();
 			this.splitContainer1.Panel2.SuspendLayout();
@@ -59,11 +60,12 @@
 			this.splitContainer2.SuspendLayout();
 			this.canvasStrip.SuspendLayout();
 			((System.ComponentModel.ISupportInitialize)(this.sliderZoom)).BeginInit();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// splitContainer1
 			// 
-			this.splitContainer1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
+			this.splitContainer1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
 			this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.splitContainer1.Location = new System.Drawing.Point(0, 0);
 			this.splitContainer1.Name = "splitContainer1";
@@ -74,12 +76,10 @@
 			// 
 			// splitContainer1.Panel2
 			// 
-			this.splitContainer1.Panel2.Controls.Add(this.canvasStrip);
+			this.splitContainer1.Panel2.Controls.Add(this.skinnedPanel1);
 			this.splitContainer1.Panel2.Controls.Add(this.canvas);
-			this.splitContainer1.Panel2.Controls.Add(this.label1);
-			this.splitContainer1.Panel2.Controls.Add(this.lblZoom);
-			this.splitContainer1.Panel2.Controls.Add(this.sliderZoom);
 			this.splitContainer1.Size = new System.Drawing.Size(1101, 516);
+			this.splitContainer1.SplitterColor = Desktop.Skinning.SkinnedBackgroundType.Primary;
 			this.splitContainer1.SplitterDistance = 297;
 			this.splitContainer1.TabIndex = 10;
 			// 
@@ -97,38 +97,33 @@
 			// splitContainer2.Panel2
 			// 
 			this.splitContainer2.Panel2.Controls.Add(this.propertyTable);
-			this.splitContainer2.Size = new System.Drawing.Size(293, 512);
-			this.splitContainer2.SplitterDistance = 302;
+			this.splitContainer2.Size = new System.Drawing.Size(295, 514);
+			this.splitContainer2.SplitterDistance = 303;
 			this.splitContainer2.TabIndex = 0;
 			// 
-			// treeScenes
-			// 
-			this.treeScenes.Dock = System.Windows.Forms.DockStyle.Fill;
-			this.treeScenes.Enabled = false;
-			this.treeScenes.Location = new System.Drawing.Point(0, 0);
-			this.treeScenes.Name = "treeScenes";
-			this.treeScenes.Size = new System.Drawing.Size(293, 302);
-			this.treeScenes.TabIndex = 0;
-			this.treeScenes.AfterSelect += new System.EventHandler<SPNATI_Character_Editor.Controls.SceneTreeEventArgs>(this.TreeScenes_AfterSelect);
-			// 
 			// propertyTable
 			// 
 			this.propertyTable.AllowDelete = false;
 			this.propertyTable.AllowFavorites = false;
 			this.propertyTable.AllowHelp = true;
 			this.propertyTable.AllowMacros = false;
+			this.propertyTable.BackColor = System.Drawing.Color.White;
 			this.propertyTable.Data = null;
 			this.propertyTable.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.propertyTable.HeaderType = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.propertyTable.HideAddField = true;
 			this.propertyTable.HideSpeedButtons = true;
 			this.propertyTable.Location = new System.Drawing.Point(0, 0);
+			this.propertyTable.ModifyingProperty = null;
 			this.propertyTable.Name = "propertyTable";
+			this.propertyTable.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.propertyTable.PlaceholderText = "Add a property";
 			this.propertyTable.PreserveControls = true;
+			this.propertyTable.PreviewData = null;
 			this.propertyTable.RemoveCaption = "Remove";
 			this.propertyTable.RowHeaderWidth = 85F;
 			this.propertyTable.RunInitialAddEvents = false;
-			this.propertyTable.Size = new System.Drawing.Size(293, 206);
+			this.propertyTable.Size = new System.Drawing.Size(295, 207);
 			this.propertyTable.Sorted = true;
 			this.propertyTable.TabIndex = 0;
 			this.propertyTable.UndoManager = null;
@@ -148,10 +143,11 @@
             this.toolStripSeparator2,
             this.cmdPlayDirective,
             this.cmdPlay});
-			this.canvasStrip.Location = new System.Drawing.Point(184, 0);
+			this.canvasStrip.Location = new System.Drawing.Point(184, 1);
 			this.canvasStrip.Name = "canvasStrip";
 			this.canvasStrip.Size = new System.Drawing.Size(192, 25);
 			this.canvasStrip.TabIndex = 0;
+			this.canvasStrip.Tag = "PrimaryLight";
 			// 
 			// cmdLock
 			// 
@@ -238,10 +234,12 @@
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.canvas.BackColor = System.Drawing.SystemColors.ControlDarkDark;
-			this.canvas.Location = new System.Drawing.Point(-2, 28);
+			this.canvas.Location = new System.Drawing.Point(0, 28);
 			this.canvas.Name = "canvas";
-			this.canvas.Size = new System.Drawing.Size(795, 486);
+			this.canvas.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.canvas.Size = new System.Drawing.Size(799, 486);
 			this.canvas.TabIndex = 0;
+			this.canvas.TabSide = Desktop.Skinning.TabSide.None;
 			this.canvas.TabStop = true;
 			this.canvas.Paint += new System.Windows.Forms.PaintEventHandler(this.Canvas_Paint);
 			this.canvas.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Canvas_MouseDown);
@@ -252,7 +250,11 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(3, 5);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(3, 6);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(37, 13);
 			this.label1.TabIndex = 11;
@@ -261,7 +263,11 @@
 			// lblZoom
 			// 
 			this.lblZoom.AutoSize = true;
-			this.lblZoom.Location = new System.Drawing.Point(148, 5);
+			this.lblZoom.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblZoom.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblZoom.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblZoom.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblZoom.Location = new System.Drawing.Point(148, 6);
 			this.lblZoom.Name = "lblZoom";
 			this.lblZoom.Size = new System.Drawing.Size(33, 13);
 			this.lblZoom.TabIndex = 13;
@@ -269,20 +275,26 @@
 			// 
 			// sliderZoom
 			// 
-			this.sliderZoom.AutoSize = false;
-			this.sliderZoom.LargeChange = 1;
-			this.sliderZoom.Location = new System.Drawing.Point(46, 5);
+			this.sliderZoom.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.sliderZoom.Increment = 1;
+			this.sliderZoom.Location = new System.Drawing.Point(46, 4);
 			this.sliderZoom.Maximum = 11;
+			this.sliderZoom.Minimum = 0;
 			this.sliderZoom.Name = "sliderZoom";
 			this.sliderZoom.Size = new System.Drawing.Size(104, 19);
 			this.sliderZoom.TabIndex = 12;
+			this.sliderZoom.TickFrequency = 0;
 			this.sliderZoom.Value = 3;
 			this.sliderZoom.ValueChanged += new System.EventHandler(this.SliderZoom_ValueChanged);
 			// 
 			// lblCoord
 			// 
 			this.lblCoord.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblCoord.Location = new System.Drawing.Point(923, 7);
+			this.lblCoord.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblCoord.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblCoord.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblCoord.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblCoord.Location = new System.Drawing.Point(623, 5);
 			this.lblCoord.Name = "lblCoord";
 			this.lblCoord.RightToLeft = System.Windows.Forms.RightToLeft.No;
 			this.lblCoord.Size = new System.Drawing.Size(175, 13);
@@ -295,18 +307,42 @@
 			this.tmrPlay.Interval = 32;
 			this.tmrPlay.Tick += new System.EventHandler(this.tmrPlay_Tick);
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedPanel1.Controls.Add(this.lblCoord);
+			this.skinnedPanel1.Controls.Add(this.canvasStrip);
+			this.skinnedPanel1.Controls.Add(this.label1);
+			this.skinnedPanel1.Controls.Add(this.sliderZoom);
+			this.skinnedPanel1.Controls.Add(this.lblZoom);
+			this.skinnedPanel1.Location = new System.Drawing.Point(-2, 0);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
+			this.skinnedPanel1.Size = new System.Drawing.Size(801, 28);
+			this.skinnedPanel1.TabIndex = 14;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// treeScenes
+			// 
+			this.treeScenes.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.treeScenes.Enabled = false;
+			this.treeScenes.Location = new System.Drawing.Point(0, 0);
+			this.treeScenes.Name = "treeScenes";
+			this.treeScenes.Size = new System.Drawing.Size(295, 303);
+			this.treeScenes.TabIndex = 0;
+			this.treeScenes.AfterSelect += new System.EventHandler<SPNATI_Character_Editor.Controls.SceneTreeEventArgs>(this.TreeScenes_AfterSelect);
+			// 
 			// EpilogueCanvas
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.lblCoord);
 			this.Controls.Add(this.splitContainer1);
 			this.Enabled = false;
 			this.Name = "EpilogueCanvas";
 			this.Size = new System.Drawing.Size(1101, 516);
 			this.splitContainer1.Panel1.ResumeLayout(false);
 			this.splitContainer1.Panel2.ResumeLayout(false);
-			this.splitContainer1.Panel2.PerformLayout();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
 			this.splitContainer1.ResumeLayout(false);
 			this.splitContainer2.Panel1.ResumeLayout(false);
@@ -316,17 +352,19 @@
 			this.canvasStrip.ResumeLayout(false);
 			this.canvasStrip.PerformLayout();
 			((System.ComponentModel.ISupportInitialize)(this.sliderZoom)).EndInit();
+			this.skinnedPanel1.ResumeLayout(false);
+			this.skinnedPanel1.PerformLayout();
 			this.ResumeLayout(false);
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.SplitContainer splitContainer1;
+		private Desktop.Skinning.SkinnedSplitContainer splitContainer1;
 		private System.Windows.Forms.SplitContainer splitContainer2;
 		private Desktop.CommonControls.SelectablePanel canvas;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label lblCoord;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel lblCoord;
 		private Desktop.CommonControls.PropertyTable propertyTable;
 		private SceneTree treeScenes;
 		private System.Windows.Forms.ToolStrip canvasStrip;
@@ -338,8 +376,9 @@
 		private System.Windows.Forms.ToolStripButton cmdPlayDirective;
 		private System.Windows.Forms.ToolStripButton cmdToggleFade;
 		private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
-		private System.Windows.Forms.Label lblZoom;
-		private System.Windows.Forms.TrackBar sliderZoom;
+		private Desktop.Skinning.SkinnedLabel lblZoom;
+		private Desktop.Skinning.SkinnedSlider sliderZoom;
 		private System.Windows.Forms.ToolStripButton cmdMarkers;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.cs b/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.cs
index 4392041fc475a0e3cc46e580e29cd0b202e48893..0ccd49bfb2e2539cc0e25e99000e39b20d8cd241 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.cs	
@@ -9,10 +9,11 @@ using SPNATI_Character_Editor.EpilogueEditing;
 using SPNATI_Character_Editor.Properties;
 using System.Globalization;
 using SPNATI_Character_Editor.Forms;
+using Desktop.Skinning;
 
 namespace SPNATI_Character_Editor.Controls
 {
-	public partial class EpilogueCanvas : UserControl
+	public partial class EpilogueCanvas : UserControl, ISkinControl
 	{
 		/// <summary>
 		/// How many pixels the user has to click within to select a handle
@@ -29,8 +30,10 @@ namespace SPNATI_Character_Editor.Controls
 		private Scene _selectedScene;
 		private Directive _selectedDirective;
 		private Keyframe _selectedKeyframe;
+		private Choice _selectedChoice;
 		private SceneAnimation _selectedAnimation;
 		private List<PendedDirective> _pendingDirectives = new List<PendedDirective>();
+		private Directive _currentPrompt = null;
 
 		private bool _viewportLocked;
 		private Point _prelockOffset;
@@ -242,6 +245,28 @@ namespace SPNATI_Character_Editor.Controls
 					g.ResetTransform();
 				}
 			}
+
+			if (_currentPrompt != null)
+			{
+				const int Padding = 10;
+				SizeF measuredTitle = g.MeasureString(_currentPrompt.Title, _font);
+				int lineHeight = (int)measuredTitle.Height;
+				int promptHeight = lineHeight * (_currentPrompt.Choices.Count + 1) + Padding * (_currentPrompt.Choices.Count - 1) + Padding * 2;
+
+				int startY = canvas.Height / 2 - promptHeight / 2;
+				int promptWidth = canvas.Width - Padding * 2;
+				g.FillRectangle(Brushes.LightGray, Padding, startY, canvas.Width - Padding * 2, promptHeight);
+				g.DrawRectangle(Pens.Black, Padding, startY, promptWidth, promptHeight);
+				StringFormat sf = new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
+				g.DrawString(_currentPrompt.Title, _font, Brushes.Black, new Rectangle(Padding, startY, promptWidth, lineHeight), sf);
+				foreach (Choice choice in _currentPrompt.Choices)
+				{
+					startY += lineHeight + Padding;
+					g.FillRectangle(Brushes.SkyBlue, Padding + Padding, startY, promptWidth - Padding * 2, lineHeight);
+					g.DrawRectangle(Pens.Black, Padding + Padding, startY, promptWidth - Padding * 2, lineHeight);
+					g.DrawString(choice.Caption, _font, Brushes.Black, new Rectangle(Padding + Padding, startY, promptWidth - Padding * 2, lineHeight), sf);
+				}
+			}
 		}
 
 		private void DrawAnimation(Graphics g, SceneObject obj, bool filled)
@@ -747,11 +772,11 @@ namespace SPNATI_Character_Editor.Controls
 				((HandledMouseEventArgs)e).Handled = true;
 				if (e.Delta > 0 && sliderZoom.Value < sliderZoom.Maximum)
 				{
-					sliderZoom.Value += sliderZoom.SmallChange;
+					sliderZoom.Value += sliderZoom.Increment;
 				}
 				else if (e.Delta < 0 && sliderZoom.Value > sliderZoom.Minimum)
 				{
-					sliderZoom.Value -= sliderZoom.SmallChange;
+					sliderZoom.Value -= sliderZoom.Increment;
 				}
 			}
 		}
@@ -857,7 +882,40 @@ namespace SPNATI_Character_Editor.Controls
 				case EditMode.Playback:
 					if (e.Button == MouseButtons.Left)
 					{
-						AdvanceDirective();
+						if (_currentPrompt != null)
+						{
+							int y = e.Y;
+							using (Graphics g = Graphics.FromHwnd(canvas.Handle))
+							{
+								const int Padding = 10;
+								SizeF measuredTitle = g.MeasureString(_currentPrompt.Title, _font);
+								int lineHeight = (int)measuredTitle.Height;
+								int promptHeight = lineHeight * (_currentPrompt.Choices.Count + 1) + Padding * (_currentPrompt.Choices.Count - 1) + Padding * 2;
+
+								int startY = canvas.Height / 2 - promptHeight / 2;
+								int endY = startY + promptHeight;
+								startY += lineHeight + Padding; //skip past the title
+								if (y >= startY && y < endY)
+								{
+									y -= startY;
+									foreach (Choice choice in _currentPrompt.Choices)
+									{
+										if (y <= lineHeight)
+										{
+											//clicked this button
+											PerformUserAction(choice);
+											break;
+										}
+										y -= lineHeight + Padding;
+									}
+								}
+							}
+
+						}
+						else
+						{
+							AdvanceDirective();
+						}
 					}
 					break;
 			}
@@ -1584,6 +1642,7 @@ namespace SPNATI_Character_Editor.Controls
 			_selectedScene = e.Scene;
 			_selectedDirective = e.Directive;
 			_selectedKeyframe = e.Keyframe;
+			_selectedChoice = e.Choice;
 
 			propertyTable.Context = new EpilogueContext(_character, _epilogue, _selectedScene);
 
@@ -1591,11 +1650,19 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				propertyTable.RecordFilter = DirectiveFilter;
 				propertyTable.Data = e.Keyframe;
+				propertyTable.RunFilter(HideRow);
+			}
+			else if (e.Choice != null)
+			{
+				propertyTable.RecordFilter = null;
+				propertyTable.Data = e.Choice;
+				propertyTable.RunFilter(ChoiceFilter);
 			}
 			else if (e.Directive != null)
 			{
 				propertyTable.RecordFilter = DirectiveFilter;
 				propertyTable.Data = e.Directive;
+				propertyTable.RunFilter(HideRow);
 			}
 			else if (e.Scene != null)
 			{
@@ -1607,7 +1674,6 @@ namespace SPNATI_Character_Editor.Controls
 				propertyTable.RecordFilter = null;
 				propertyTable.Data = null;
 			}
-			propertyTable.RunFilter(HideRow);
 
 			BuildScene(false);
 			if (_selectedScene != oldScene)
@@ -1644,6 +1710,28 @@ namespace SPNATI_Character_Editor.Controls
 			return true;
 		}
 
+		private bool ChoiceFilter(PropertyRecord record, object data, object context)
+		{
+			if (_selectedChoice == null)
+			{
+				return true;
+			}
+			if (record.Key == "action" || record.Key == "text")
+			{
+				return true;
+			}
+
+			switch (_selectedChoice.Action)
+			{
+				case "jump":
+					return record.Key == "id";
+				case "marker":
+					return true;
+				default:
+					return false;
+			}
+		}
+
 		private static readonly string[] TransitionProperties = new string[] { "effect", "ease", "time" }; //hardcoding allowed properties for now since I don't expect this to change
 		private bool SceneFilter(PropertyRecord record)
 		{
@@ -1684,6 +1772,10 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				propertyTable.RunFilter(HideRow);
 			}
+			else if (e.PropertyName == "Action")
+			{
+				propertyTable.RunFilter(ChoiceFilter);
+			}
 		}
 
 		private bool HideRow(PropertyRecord record, object data, object context)
@@ -1818,22 +1910,48 @@ namespace SPNATI_Character_Editor.Controls
 			}
 		}
 
+		private void PerformUserAction(Choice choice)
+		{
+			switch (choice.Action)
+			{
+				case "jump":
+					_directiveIndex = _selectedScene.Directives.Count;
+					AdvanceDirective();
+					break;
+				case "marker":
+					if (!string.IsNullOrEmpty(choice.Id))
+					{
+						string value = choice.Value ?? "1";
+						_scenePreview.Markers[choice.Id] = value;
+					}
+					AdvanceDirective();
+					break;
+				default:
+					AdvanceDirective();
+					break;
+			}
+		}
+
 		private void PerformDirective()
 		{
 			_directiveIndex++;
 			if (_directiveIndex < _selectedScene.Directives.Count)
 			{
 				Directive directive = _selectedScene.Directives[_directiveIndex];
-				if (!string.IsNullOrEmpty(directive.Delay) && directive.Delay != "0")
-				{
-					float delay;
-					float.TryParse(directive.Delay, NumberStyles.Number, CultureInfo.InvariantCulture, out delay);
-					delay *= 1000;
-					PendDirective(directive, delay);
-				}
-				else if (!ApplyDirective(directive))
+
+				if (string.IsNullOrEmpty(directive.Marker) || Marker.CheckMarker(directive.Marker, _scenePreview.Markers))
 				{
-					return;
+					if (!string.IsNullOrEmpty(directive.Delay) && directive.Delay != "0")
+					{
+						float delay;
+						float.TryParse(directive.Delay, NumberStyles.Number, CultureInfo.InvariantCulture, out delay);
+						delay *= 1000;
+						PendDirective(directive, delay);
+					}
+					else if (!ApplyDirective(directive))
+					{
+						return;
+					}
 				}
 				PerformDirective();
 			}
@@ -1845,6 +1963,7 @@ namespace SPNATI_Character_Editor.Controls
 		/// <param name="directive"></param>
 		private bool ApplyDirective(Directive directive)
 		{
+			_currentPrompt = null;
 			SceneObject obj = null;
 			if (directive.Id != null)
 			{
@@ -2031,6 +2150,15 @@ namespace SPNATI_Character_Editor.Controls
 						}
 					}
 					break;
+				case "jump":
+					if (_mode == EditMode.Playback)
+					{
+						_directiveIndex = _selectedScene.Directives.Count;
+					}
+					return false;
+				case "prompt":
+					_currentPrompt = directive;
+					return false;
 			}
 
 			if (_mode == EditMode.Edit)
@@ -2422,6 +2550,7 @@ namespace SPNATI_Character_Editor.Controls
 
 		private void AdvanceDirective()
 		{
+			_currentPrompt = null;
 			HaltAnimations(false);
 
 			if (_directiveIndex >= _selectedScene.Directives.Count)
@@ -2486,12 +2615,17 @@ namespace SPNATI_Character_Editor.Controls
 				BuildScene(false);
 			}
 		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			
+		}
 	}
 
 	public class EpilogueContext : IAutoCompleteList, ICharacterContext
 	{
 		public ISkin Character { get; set; }
-		public CharacterContext Context { get; private set; }
+		public CharacterContext Context { get; set; }
 		public Epilogue Epilogue { get; set; }
 		public Scene Scene { get; set; }
 
@@ -2516,6 +2650,7 @@ namespace SPNATI_Character_Editor.Controls
 				return null;
 			}
 			HashSet<string> sourceType = new HashSet<string>();
+			HashSet<string> items = new HashSet<string>();
 			bool allowCamera = false;
 			bool allowFade = false;
 			if (dir.DirectiveType == "move" || dir.DirectiveType == "stop" || dir.DirectiveType == "remove" || dir.DirectiveType == "emit")
@@ -2533,12 +2668,18 @@ namespace SPNATI_Character_Editor.Controls
 			{
 				sourceType.Add("text");
 			}
-			if (sourceType.Count == 0)
+			else if (dir.DirectiveType == "jump")
 			{
-				return null;
+				foreach (Scene s in Epilogue.Scenes)
+				{
+					string id = s.Id;
+					if (!string.IsNullOrEmpty(id))
+					{
+						items.Add(id);
+					}
+				}
 			}
-			HashSet<string> items = new HashSet<string>();
-			if (Scene != null)
+			if (sourceType.Count > 0 && Scene != null)
 			{
 				if (allowCamera)
 				{
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.resx b/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.resx
index 5fe1edfdebc710b70e6f0e176d30ab5edae03379..75f58dc72ca50dc02a4d1c797fbd9af002b3a69f 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.resx	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/EpilogueCanvas.resx	
@@ -120,6 +120,9 @@
   <metadata name="canvasStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>17, 17</value>
   </metadata>
+  <metadata name="canvasStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
   <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
   <data name="cmdMarkers.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
     <value>
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ICanInvalidate.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ICanInvalidate.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0a69a4f6d619db1c7eb33752248e13d3826d89ee
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ICanInvalidate.cs	
@@ -0,0 +1,9 @@
+using System;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	interface ICanInvalidate
+	{
+		event EventHandler Invalidated;
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ICanvasViewport.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ICanvasViewport.cs
new file mode 100644
index 0000000000000000000000000000000000000000..85890bad8f4aafc5a73ce104a43d5afd59c797ca
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ICanvasViewport.cs	
@@ -0,0 +1,12 @@
+using System;
+using System.Drawing;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public interface ICanvasViewport
+	{
+		event EventHandler ViewportUpdated;
+		void FitToViewport(int windowWidth, int windowHeight, ref Point offset, ref float zoom);
+		bool AllowPan { get; }
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineAction.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineAction.cs
index 508f7905459d120121ee98fca180005d93a54b33..919bda48951cab3d4f075bdc851f47ebdbe49edc 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineAction.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineAction.cs	
@@ -35,7 +35,7 @@ namespace SPNATI_Character_Editor
 		/// <summary>
 		/// Selected widget
 		/// </summary>
-		public ITimelineWidget Widget;
+		public ITimelineObject Widget;
 		/// <summary>
 		/// Selected time
 		/// </summary>
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineBreak.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineBreak.cs
new file mode 100644
index 0000000000000000000000000000000000000000..94c77bd14893b416ab4eeeb8ba543d21818f1b9a
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineBreak.cs	
@@ -0,0 +1,12 @@
+using System.Drawing;
+using Desktop.Skinning;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public interface ITimelineBreak : ITimelineObject
+	{
+		float Time { get; set; }
+		void DrawBackground(Graphics g, float pps, int height, bool selected);
+		void Draw(Graphics g, float pps, int height, bool selected);
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineData.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineData.cs
index 76066c97967c345b8d42a3515801a9905b2f1eab..e40b4b661bd5aa8fc0792b6ea735a92c482cb82a 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineData.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineData.cs	
@@ -11,20 +11,22 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		List<ITimelineWidget> CreateWidgets(Timeline timeline);
 		ITimelineWidget CreateWidget(Timeline timeline, float time, object context);
 		ITimelineWidget CreateWidget(Timeline timeline, float time, object data, int index);
-		void InsertWidget(ITimelineWidget widget, float time, int index);
-		int RemoveWidget(ITimelineWidget widget);
-		void MoveWidget(ITimelineWidget widget, int newTrack);
+		void InsertWidget(ITimelineObject widget, float time, int index);
+		int RemoveWidget(ITimelineObject widget);
+		void MoveWidget(ITimelineObject widget, int newTrack);
 		bool OnPaste(WidgetOperationArgs args);
 		///Called when no widget is selcted
 		void UpdateSelection(WidgetSelectionArgs args);
+		List<ITimelineBreak> CreateBreaks(Timeline timeline);
+		ITimelineBreak AddBreak(float time);
 	}
 
 	public class WidgetCreationArgs : EventArgs
 	{
-		public ITimelineWidget Widget;
+		public ITimelineObject Widget;
 		public int Index;
 
-		public WidgetCreationArgs(ITimelineWidget widget, int index)
+		public WidgetCreationArgs(ITimelineObject widget, int index)
 		{
 			Widget = widget;
 			Index = index;
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineObject.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineObject.cs
new file mode 100644
index 0000000000000000000000000000000000000000..18dbe60c2e83d52dbe9a23c121f426b284d17668
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineObject.cs	
@@ -0,0 +1,78 @@
+using Desktop.Skinning;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public interface ITimelineObject
+	{
+		object GetData();
+		/// <summary>
+		/// Gets widget's starting point in seconds
+		/// </summary>
+		/// <returns></returns>
+		float GetStart();
+		void SetStart(float time);
+		void OnWidgetSelectionChanged(WidgetSelectionArgs args);
+		void UpdateSkin(Skin skin);
+		/// <summary>
+		/// Gets the action that should occur if the mouse is click on what it's currently hovering over
+		/// </summary>
+		/// <param name="x"></param>
+		/// <param name="width"></param>
+		/// <param name="start"></param>
+		/// <param name="row"></param>
+		/// <param name="timelineWidth"></param>
+		/// <param name="pps"></param>
+		/// <returns></returns>
+		ITimelineAction GetAction(int x, float start, int row, int timelineWidth, float pps);
+		/// <summary>
+		/// Called on the selected widget after an undo or redo operation to clear the selection properly if it got deleted
+		/// </summary>
+		void UpdateSelection(WidgetSelectionArgs args);
+		/// <summary>
+		/// Called when performing a copy operation while the widget is selected
+		/// </summary>
+		/// <returns></returns>
+		bool OnCopy(WidgetOperationArgs args);
+		/// <summary>
+		/// Called when performing a delete operation while the widget is selected
+		/// </summary>
+		/// <returns></returns>
+		bool OnDelete(WidgetOperationArgs args);
+		/// <summary>
+		/// Called when performing a paste operation while the widget is selected
+		/// </summary>
+		/// <returns></returns>
+		bool OnPaste(WidgetOperationArgs args);
+		/// <summary>
+		/// Called when performing a duplicate operation while the widget is selected
+		/// </summary>
+		/// <returns></returns>
+		bool OnDuplicate(WidgetOperationArgs args);
+		/// <summary>
+		/// Called on the selected widget when displaying the right-click menu
+		/// </summary>
+		/// <param name="args"></param>
+		void OnOpeningContextMenu(ContextMenuArgs args);
+		/// <summary>
+		/// Advances the selection forward or backwards within the widget
+		/// </summary>
+		/// <param name="forward"></param>
+		void AdvanceSubWidget(bool forward);
+		/// <summary>
+		/// Called when the current time is manually changed
+		/// </summary>
+		/// <param name="time"></param>
+		void OnTimeChanged(WidgetOperationArgs args);
+		/// <summary>
+		/// Called when double-clicking the main widget area
+		/// </summary>
+		/// <param name="args"></param>
+		void OnDoubleClick(WidgetActionArgs args);
+		/// <summary>
+		/// Called when clicking on a widget to move it instead of something inside of it
+		/// </summary>
+		/// <param name="args"></param>
+		void OnStartMove(WidgetActionArgs args);
+		void OnMouseOut();
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineWidget.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineWidget.cs
index 9704eb1d3bc28bfce6ac992ef9c0344fb12f4955..8227ebe0230092053ef47a0316dbab886d39560d 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineWidget.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Interfaces/ITimelineWidget.cs	
@@ -1,25 +1,17 @@
 using Desktop;
+using Desktop.Skinning;
 using System;
 using System.Drawing;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.EpilogueEditor
 {
-	public interface ITimelineWidget
+	public interface ITimelineWidget : ITimelineObject
 	{
-		object GetData();
-		void OnWidgetSelectionChanged(WidgetSelectionArgs args);
 		event EventHandler Invalidated;
-		void DrawContents(Graphics g, int rowIndex, int x, int y, float pps, int widgetWidth, int rowHeight);
+		void DrawContents(Graphics g, int rowIndex, int x, int y, float pps, int rowHeight);
 		string GetLabel(int row);
 		int GetRowCount();
-		/// <summary>
-		/// Gets widget's starting point in seconds
-		/// </summary>
-		/// <returns></returns>
-		float GetStart();
-		void SetStart(float time);
-		Brush GetFillBrush();
 		Image GetThumbnail();
 		/// <summary>
 		/// Gets widget's length in seconds
@@ -27,15 +19,8 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		/// <param name="duration">Duration of the entire animation</param>
 		/// <returns></returns>
 		float GetLength(float duration);
-		/// <summary>
-		/// Sets the widget's length in seconds
-		/// </summary>
-		/// <param name="time"></param>
-		/// <returns></returns>
-		void SetLength(float time);
 		bool IsCollapsible { get; }
 		bool IsCollapsed { get; set; }
-		bool IsResizable { get; }
 		/// <summary>
 		/// Gets whether a row should be highlighted on the selected widget
 		/// </summary>
@@ -43,26 +28,16 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		/// <returns></returns>
 		bool IsRowHighlighted(int row);
 		/// <summary>
-		/// Gets or sets whether the widget's length is tied to the end of the whole timeline's duration
-		/// </summary>
-		bool LinkedToEnd { get; }
-		/// <summary>
-		/// Gets the action that should occur if the mouse is click on what it's currently hovering over
-		/// </summary>
-		/// <param name="x"></param>
-		/// <param name="width"></param>
-		/// <param name="start"></param>
-		/// <param name="row"></param>
-		/// <param name="timelineWidth"></param>
-		/// <param name="pps"></param>
-		/// <returns></returns>
-		ITimelineAction GetAction(int x, int width, float start, int row, int timelineWidth, float pps);
-		/// <summary>
 		/// Called on a widget when its header is clicked
 		/// </summary>
 		/// <param name="args"></param>
 		void OnClickHeader(WidgetActionArgs args);
 		/// <summary>
+		/// Called on a widget when its header is double-clicked
+		/// </summary>
+		/// <param name="args"></param>
+		void OnDoubleClickHeader(WidgetActionArgs args);
+		/// <summary>
 		/// Called when clicking a header icon. OnClickHeader will still be called immediately after this
 		/// </summary>
 		/// <param name="args"></param>
@@ -73,41 +48,6 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		/// <param name="args"></param>
 		/// <returns></returns>
 		string GetHeaderTooltip(WidgetActionArgs args, int iconIndex);
-		/// <summary>
-		/// Called on the selected widget after an undo or redo operation to clear the selection properly if it got deleted
-		/// </summary>
-		void UpdateSelection(WidgetSelectionArgs args);
-		/// <summary>
-		/// Called when performing a copy operation while the widget is selected
-		/// </summary>
-		/// <returns></returns>
-		bool OnCopy(WidgetOperationArgs args);
-		/// <summary>
-		/// Called when performing a delete operation while the widget is selected
-		/// </summary>
-		/// <returns></returns>
-		bool OnDelete(WidgetOperationArgs args);
-		/// <summary>
-		/// Called when performing a paste operation while the widget is selected
-		/// </summary>
-		/// <returns></returns>
-		bool OnPaste(WidgetOperationArgs args);
-		/// <summary>
-		/// Called when performing a duplicate operation while the widget is selected
-		/// </summary>
-		/// <returns></returns>
-		bool OnDuplicate(WidgetOperationArgs args);
-		/// <summary>
-		/// Called when clicking on a widget to move it instead of something inside of it
-		/// </summary>
-		/// <param name="args"></param>
-		void OnStartMove(WidgetActionArgs args);
-		/// <summary>
-		/// Called when the current time is manually changed
-		/// </summary>
-		/// <param name="time"></param>
-		void OnTimeChanged(WidgetOperationArgs args);
-		void OnMouseOut();
 		void OnPlaybackChanged(bool playing);
 		/// <summary>
 		/// Gets the number of icons that can display in a row header
@@ -123,16 +63,6 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		/// <param name="y"></param>
 		/// <param name="rowHeight"></param>
 		void DrawHeaderIcon(Graphics g, int rowIndex, int iconIndex, int x, int y, int size, int highlightedIconIndex);
-		/// <summary>
-		/// Advances the selection forward or backwards within the widget
-		/// </summary>
-		/// <param name="forward"></param>
-		void AdvanceSubWidget(bool forward);
-		/// <summary>
-		/// Called on the selected widget when displaying the right-click menu
-		/// </summary>
-		/// <param name="args"></param>
-		void OnOpeningContextMenu(ContextMenuArgs args);
 	}
 
 	public class WidgetOperationArgs
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/LiveCanvas.Designer.cs b/editor source/SPNATI Character Editor/EpilogueEditing/LiveCanvas.Designer.cs
index 25e538607dd90469097708259ed906ba9afb2729..ab4108aef717a2e1213aa847f3a93bac4b8f62ab 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/LiveCanvas.Designer.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/LiveCanvas.Designer.cs	
@@ -40,11 +40,13 @@
 			this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
 			this.tsRecord = new System.Windows.Forms.ToolStripButton();
 			this.tsRight = new System.Windows.Forms.ToolStrip();
-			this.tsHelp = new System.Windows.Forms.ToolStripButton();
 			this.tsBackColor = new System.Windows.Forms.ToolStripButton();
+			this.tsHelp = new System.Windows.Forms.ToolStripButton();
 			this.colorDialog1 = new System.Windows.Forms.ColorDialog();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
 			this.canvasStrip.SuspendLayout();
 			this.tsRight.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// canvas
@@ -56,8 +58,10 @@
 			this.canvas.Location = new System.Drawing.Point(0, 25);
 			this.canvas.Margin = new System.Windows.Forms.Padding(0);
 			this.canvas.Name = "canvas";
+			this.canvas.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.canvas.Size = new System.Drawing.Size(851, 589);
 			this.canvas.TabIndex = 0;
+			this.canvas.TabSide = Desktop.Skinning.TabSide.None;
 			this.canvas.TabStop = true;
 			this.canvas.Paint += new System.Windows.Forms.PaintEventHandler(this.canvas_Paint);
 			this.canvas.MouseDown += new System.Windows.Forms.MouseEventHandler(this.canvas_MouseDown);
@@ -83,6 +87,7 @@
 			this.canvasStrip.Name = "canvasStrip";
 			this.canvasStrip.Size = new System.Drawing.Size(207, 25);
 			this.canvasStrip.TabIndex = 19;
+			this.canvasStrip.Tag = "PrimaryLight";
 			// 
 			// cmdFit
 			// 
@@ -179,6 +184,18 @@
 			this.tsRight.RightToLeft = System.Windows.Forms.RightToLeft.No;
 			this.tsRight.Size = new System.Drawing.Size(80, 25);
 			this.tsRight.TabIndex = 20;
+			this.tsRight.Tag = "PrimaryLight";
+			// 
+			// tsBackColor
+			// 
+			this.tsBackColor.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsBackColor.Image = global::SPNATI_Character_Editor.Properties.Resources.ColorPalette;
+			this.tsBackColor.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsBackColor.Name = "tsBackColor";
+			this.tsBackColor.Size = new System.Drawing.Size(23, 22);
+			this.tsBackColor.Text = "Background Color";
+			this.tsBackColor.ToolTipText = "Set background color";
+			this.tsBackColor.Click += new System.EventHandler(this.tsBackColor_Click);
 			// 
 			// tsHelp
 			// 
@@ -191,23 +208,24 @@
 			this.tsHelp.ToolTipText = "Show Help...";
 			this.tsHelp.Click += new System.EventHandler(this.tsHelp_Click);
 			// 
-			// tsBackColor
+			// skinnedPanel1
 			// 
-			this.tsBackColor.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
-			this.tsBackColor.Image = global::SPNATI_Character_Editor.Properties.Resources.ColorPalette;
-			this.tsBackColor.ImageTransparentColor = System.Drawing.Color.Magenta;
-			this.tsBackColor.Name = "tsBackColor";
-			this.tsBackColor.Size = new System.Drawing.Size(23, 22);
-			this.tsBackColor.Text = "Background Color";
-			this.tsBackColor.ToolTipText = "Set background color";
-			this.tsBackColor.Click += new System.EventHandler(this.tsBackColor_Click);
+			this.skinnedPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedPanel1.Controls.Add(this.tsRight);
+			this.skinnedPanel1.Controls.Add(this.canvasStrip);
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 0);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
+			this.skinnedPanel1.Size = new System.Drawing.Size(851, 25);
+			this.skinnedPanel1.TabIndex = 21;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
 			// 
 			// LiveCanvas
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.Controls.Add(this.tsRight);
-			this.Controls.Add(this.canvasStrip);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.canvas);
 			this.Name = "LiveCanvas";
 			this.Size = new System.Drawing.Size(851, 614);
@@ -215,8 +233,9 @@
 			this.canvasStrip.PerformLayout();
 			this.tsRight.ResumeLayout(false);
 			this.tsRight.PerformLayout();
+			this.skinnedPanel1.ResumeLayout(false);
+			this.skinnedPanel1.PerformLayout();
 			this.ResumeLayout(false);
-			this.PerformLayout();
 
 		}
 
@@ -237,5 +256,6 @@
 		private System.Windows.Forms.ToolStripButton tsFilter;
 		private System.Windows.Forms.ToolStripButton tsBackColor;
 		private System.Windows.Forms.ColorDialog colorDialog1;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/LiveCanvas.cs b/editor source/SPNATI Character Editor/EpilogueEditing/LiveCanvas.cs
index 85e126446c68a098848d5c6efab8be28c0b96ab2..c2d1f39d111d7719999d63ddd687cf459a12431c 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/LiveCanvas.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/LiveCanvas.cs	
@@ -1,26 +1,40 @@
-using Desktop;
-using SPNATI_Character_Editor.Controls;
-using SPNATI_Character_Editor.Forms;
-using SPNATI_Character_Editor.Properties;
-using System;
+using System;
 using System.Collections.Generic;
-using System.Collections.Specialized;
 using System.Drawing;
 using System.Drawing.Drawing2D;
 using System.Windows.Forms;
+using Desktop;
+using Desktop.Skinning;
+using SPNATI_Character_Editor.Controls;
+using SPNATI_Character_Editor.Forms;
+using SPNATI_Character_Editor.Properties;
+
+
+/* Todo - I'm pausing this because I'm not convinced it really makes epilogues easier to make - speech bubbles and pauses are pretty annoying with this system
+ * ----
+ * need way to define length on looped sprites (maybe use an event like bursts?)
+ * insert pauses into correct position
+ * Conversion between Scene and LiveScene
+ * Emitter bursts (add events to LiveAnimatedObject?)
+
+*/
 
 namespace SPNATI_Character_Editor.EpilogueEditor
 {
-	public partial class LiveCanvas : UserControl
+	public partial class LiveCanvas : UserControl, ISkinControl
 	{
 		public const int SelectionLeeway = 5;
 		public const int RotationLeeway = 30;
 
 		private bool _recording;
-		private bool _playing;
+		public bool Playing { get; private set; }
 		private ISkin _character;
-		private LivePose _pose;
+		private LiveData _data;
+		private ICanvasViewport _viewport;
 		private List<string> _markers = new List<string>();
+		private List<string> _userMarkers = new List<string>();
+		private List<string> _addedMarkers = new List<string>();
+		private List<string> _removedMarkers = new List<string>();
 		private bool _ignoreMarkers = false;
 
 		private Point _lastMouse;
@@ -40,12 +54,9 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		private HoverContext _moveContext;
 		private CanvasState _state = CanvasState.Normal;
 
+		private bool _backColorCustomized = false;
 		private SolidBrush _backColor = new SolidBrush(Color.LightGray);
-		private Pen _penOuterSelection;
-		private Pen _penInnerSelection;
 		private Pen _penBoundary;
-		private Pen _penKeyframe;
-		private Brush _brushHandle;
 
 		private const int DefaultZoomIndex = 3;
 		private int _zoomIndex = DefaultZoomIndex;
@@ -54,42 +65,102 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		private float _time;
 		private float _playbackTime;
+		private float _elapsedTime;
+		private bool _inChange;
 
 		public UndoManager UndoManager;
 
-		private LiveSprite _selectedObject;
-		private LiveSprite _selectionSource;
+		private LiveObject _selectedPreview;
+		private LiveObject _selectionSource;
 
 		private Matrix SceneTransform;
 
 		public event EventHandler<CanvasSelectionArgs> ObjectSelected;
+		public event EventHandler ToolBarButtonClicked;
+		public event EventHandler CanvasClicked;
+		public event EventHandler<CanvasPaintArgs> CustomPaint;
+
+		public bool AllowZoom { get; set; }
+		public bool DisallowEdit { get; set; }
+
+		private bool _customDraw;
+		public bool CustomDraw
+		{
+			get { return _customDraw; }
+			set
+			{
+				_customDraw = value;
+				foreach (Control ctl in skinnedPanel1.Controls)
+				{
+					ctl.Enabled = !_customDraw;
+				}
+				canvas.Invalidate();
+			}
+		}
 
 		public LiveCanvas()
 		{
 			InitializeComponent();
 
+			AllowZoom = true;
 			canvas.MouseWheel += Canvas_MouseWheel;
 			canvas.KeyDown += Canvas_KeyDown;
 			UpdateSceneTransform();
 		}
 
+		public int CanvasWidth
+		{
+			get { return canvas.Width; }
+		}
+		public int CanvasHeight
+		{
+			get { return canvas.Height; }
+		}
+
 		private void CleanUp()
 		{
-			if (_pose != null)
+			if (_data != null)
 			{
-				_pose.PropertyChanged -= _pose_PropertyChanged;
-				_pose = null;
+				DestroyLivePreview();
+				_data.PropertyChanged -= _data_PropertyChanged;
+				_data = null;
+				if (_viewport != null)
+				{
+					_viewport.ViewportUpdated -= _viewport_ViewportUpdated;
+					_viewport = null;
+				}
 			}
 		}
 
-		public void SetData(ISkin character, LivePose pose)
+		public void AddToolBarButton(Image icon, string tooltip, bool checkOnClick, Action<ToolStripButton> clickHandler)
+		{
+			ToolStripButton item = new ToolStripButton("", icon, CustomToolbarButton_Click);
+			item.ToolTipText = tooltip;
+			item.CheckOnClick = checkOnClick;
+			item.Tag = clickHandler;
+			canvasStrip.Items.Add(item);
+		}
+		private void CustomToolbarButton_Click(object sender, EventArgs e)
+		{
+			ToolStripButton btn = sender as ToolStripButton;
+			Action<ToolStripButton> click = btn?.Tag as Action<ToolStripButton>;
+			ToolBarButtonClicked?.Invoke(sender, e);
+			click?.Invoke(btn);
+		}
+
+		public void SetData(ISkin character, LiveData data)
 		{
 			CleanUp();
 			_character = character;
-			_pose = pose;
-			if (_pose != null)
+			_data = data;
+			if (_data != null)
 			{
-				_pose.PropertyChanged += _pose_PropertyChanged;
+				_data.PropertyChanged += _data_PropertyChanged;
+				if (_data is ICanvasViewport)
+				{
+					_viewport = _data as ICanvasViewport;
+					_viewport.ViewportUpdated += _viewport_ViewportUpdated;
+				}
 			}
 			UpdateSceneTransform();
 			canvas.Invalidate();
@@ -97,183 +168,171 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			_penBoundary = new Pen(Color.Gray, 1);
 			_penBoundary.DashStyle = DashStyle.Dash;
 
-			_brushHandle = new SolidBrush(Color.Black);
-			_penOuterSelection = new Pen(Brushes.White, 3);
-			_penOuterSelection.DashStyle = DashStyle.DashDotDot;
-			_penInnerSelection = new Pen(Brushes.Black, 1);
-			_penInnerSelection.DashStyle = DashStyle.DashDotDot;
+			UpdateTime(_time, _playbackTime, _elapsedTime);
+		}
 
-			_penKeyframe = new Pen(Color.FromArgb(127, 255, 255, 255));
-			_penKeyframe.Width = 2;
+		public void LockToolbar(bool locked, ToolStripButton excludedButton)
+		{
+			foreach (ToolStripItem item in canvasStrip.Items)
+			{
+				if (item == excludedButton) { continue; }
+				item.Enabled = !locked;
+			}
+		}
 
-			UpdateTime(_time, _playbackTime);
+		private void _viewport_ViewportUpdated(object sender, EventArgs e)
+		{
+			_viewport.FitToViewport(canvas.Width, canvas.Height, ref _canvasOffset, ref _zoom);
+			UpdateSceneTransform();
+			canvas.Invalidate();
 		}
 
 		public void SetPlayback(bool playing)
 		{
-			_playing = playing;
-			UpdateTime(_time, _playbackTime);
+			Playing = playing;
+			UpdateTime(_time, _playbackTime, _elapsedTime);
 		}
 
-		private void _pose_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		private void _data_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
 		{
+			if (_inChange) { return; }
+			_inChange = true;
 			if (e.PropertyName == "BaseHeight")
 			{
 				UpdateSceneTransform();
 			}
-			UpdatePose();
+			UpdateData();
+			_inChange = false;
 		}
 
-		public void UpdateTime(float time, float playbackTime)
+		public void UpdateTime(float time, float playbackTime, float elapsedTime)
 		{
+			_elapsedTime = elapsedTime;
 			_playbackTime = playbackTime;
 			_time = time;
-			UpdatePose();
+			UpdateData();
 		}
 
-		private void UpdatePose()
+		private void UpdateData()
 		{
-			if (_pose == null) { return; }
-			_pose.UpdateTime(_playing ? _playbackTime : _time, true);
-			_selectedObject?.Update(_time, false);
+			if (_data == null) { return; }
+			_data.UpdateTime(Playing ? _playbackTime : _time, _elapsedTime, true);
+			_selectedPreview?.Update(_time, _elapsedTime, false);
 			canvas.Invalidate();
 		}
 
 		public void SelectData(object data)
 		{
-			if (_selectionSource != null)
-			{
-				DetachSourceListener();
-			}
+			DestroyLivePreview();
 
-			LiveSprite sprite = data as LiveSprite;
-			_selectionSource = sprite;
+			LiveObject obj = data as LiveObject;
+			_selectionSource = obj;
 			canvas.Invalidate();
 
 			if (_selectionSource != null)
 			{
-				AttachSourceListener();
-			}
-			CreateSelectionPreview();
-		}
-
-		private void CreateSelectionPreview()
-		{
-			if (_selectedObject != null)
-			{
-				DetachPreviewListener();
-				_selectedObject = null;
-			}
-			if (_selectionSource == null) { return; }
-
-			_selectedObject = _selectionSource.Copy();
-			_selectedObject.Pose = _selectionSource.Pose;
-			_selectedObject.Hidden = false;
-			if (_selectionSource.Keyframes.Count > 0)
-			{
-				_selectedObject.Keyframes.Clear();
-				foreach (LiveKeyframe kf in _selectionSource.Keyframes) //use the same keyframe references so we can modify them indirectly
+				CreateSelectionPreview();
+				ICanInvalidate invalidatableSource = data as ICanInvalidate;
+				if (invalidatableSource != null)
 				{
-					_selectedObject.Keyframes.Add(kf);
+					invalidatableSource.Invalidated += InvalidatableSource_Invalidated;
 				}
 			}
-			AttachPreviewListener();
-			_selectedObject.Update(_time, false);
 		}
 
-		private void _selectedObject_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		private void InvalidatableSource_Invalidated(object sender, EventArgs e)
 		{
-			DetachSourceListener();
-			if (e.PropertyName == "PivotX")
-			{
-				_selectionSource.PivotX = _selectedObject.PivotX;
-			}
-			else if (e.PropertyName == "PivotY")
-			{
-				_selectionSource.PivotY = _selectedObject.PivotY;
-			}
-			AttachSourceListener();
-		}
-
-		private void AttachSourceListener()
-		{
-			_selectionSource.Keyframes.CollectionChanged += Source_CollectionChanged;
-			_selectionSource.PropertyChanged += _selectionSource_PropertyChanged;
-		}
-
-		private void DetachSourceListener()
-		{
-			_selectionSource.Keyframes.CollectionChanged -= Source_CollectionChanged;
-			_selectionSource.PropertyChanged -= _selectionSource_PropertyChanged;
+			canvas.Invalidate();
 		}
 
-		private void AttachPreviewListener()
+		private void DestroyLivePreview()
 		{
-			_selectedObject.PropertyChanged += _selectedObject_PropertyChanged;
-			_selectedObject.Keyframes.CollectionChanged += Keyframes_CollectionChanged;
+			if (_selectionSource != null)
+			{
+				foreach (string marker in _removedMarkers)
+				{
+					if (_userMarkers.Contains(marker))
+					{
+						_markers.Add(marker);
+					}
+				}
+				_removedMarkers.Clear();
+				foreach (string marker in _addedMarkers)
+				{
+					if (!_userMarkers.Contains(marker))
+					{
+						_markers.Remove(marker);
+					}
+				}
+				_addedMarkers.Clear();
+				ICanInvalidate invalidatableSource = _selectionSource as ICanInvalidate;
+				if (invalidatableSource != null)
+				{
+					invalidatableSource.Invalidated -= InvalidatableSource_Invalidated;
+				}
+				_selectionSource.DestroyLivePreview();
+				_selectionSource.PreviewInvalidated -= _selectionSource_PreviewInvalidated;
+				_selectedPreview = null;
+			}
 		}
 
-		private void DetachPreviewListener()
+		private void _selectionSource_PreviewInvalidated(object sender, EventArgs e)
 		{
-			_selectedObject.Keyframes.CollectionChanged -= Keyframes_CollectionChanged;
-			_selectedObject.PropertyChanged -= _selectedObject_PropertyChanged;
+			CreateSelectionPreview();
 		}
 
-		private void _selectionSource_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		private void CreateSelectionPreview()
 		{
-			if (e.PropertyName == "Start")
-			{
-				_selectedObject.Start = _selectionSource.Start;
-				_selectedObject?.Update(_time, false);
-			}
-			if (e.PropertyName == "PivotX")
-			{
-				_selectedObject.PivotX = _selectionSource.PivotX;
-			}
-			else if (e.PropertyName == "PivotY")
-			{
-				_selectedObject.PivotY = _selectionSource.PivotY;
-			}
-			else if (e.PropertyName == "ParentId")
-			{
-				_selectedObject.ParentId = _selectionSource.ParentId;
-			}
-		}
+			DestroyLivePreview();
+			if (_selectionSource == null) { return; }
 
-		private void Keyframes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
-		{
-			if (e.Action == NotifyCollectionChangedAction.Add)
+			_selectionSource.PreviewInvalidated += _selectionSource_PreviewInvalidated;
+			_selectedPreview = _selectionSource.CreateLivePreview(_time);
+			if (!string.IsNullOrEmpty(_selectedPreview.Marker))
 			{
-				DetachSourceListener();
-				foreach (LiveKeyframe kf in e.NewItems)
+				MarkerOperator op;
+				string value;
+				bool perTarget;
+				string marker = Marker.ExtractConditionPieces(_selectedPreview.Marker, out op, out value, out perTarget);
+				if (value == "0" && op == MarkerOperator.Equals || value != "0" && op == MarkerOperator.NotEqual)
+				{
+					_removedMarkers.Add(marker);
+					_markers.Remove(marker);
+				}
+				else if (value != "0" && op != MarkerOperator.NotEqual && !_markers.Contains(marker))
 				{
-					_selectionSource.AddKeyframe(kf);
+					_addedMarkers.Add(marker);
+					_markers.Add(marker);
 				}
-				AttachSourceListener();
 			}
-		}
-
-		private void Source_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
-		{
-			CreateSelectionPreview();
+			canvas.Invalidate();
 		}
 
 		private void UpdateSceneTransform()
 		{
-			SceneTransform = new Matrix();
-			float screenScale = canvas.Height * _zoom / (_pose == null ? 1400 : _pose.BaseHeight);
-			SceneTransform.Scale(screenScale, screenScale, MatrixOrder.Append); // scale to display * zoom
-			SceneTransform.Translate(canvas.Width * 0.5f + _canvasOffset.X, _canvasOffset.Y, MatrixOrder.Append); // center horizontally
+			if (_data == null)
+			{
+				SceneTransform = new Matrix();
+			}
+			else
+			{
+				SceneTransform = _data.GetSceneTransform(canvas.Width, canvas.Height, _canvasOffset, _zoom);
+			}
 		}
 
 		private void canvas_Paint(object sender, PaintEventArgs e)
 		{
-			if (_pose == null)
+			Graphics g = e.Graphics;
+			if (CustomDraw)
 			{
+				CustomPaint?.Invoke(this, new CanvasPaintArgs(g, canvas.Width, canvas.Height));
 				return;
 			}
 
-			Graphics g = e.Graphics;
+			if (_data == null)
+			{
+				return;
+			}
 
 			//draw the "screen"
 			g.FillRectangle(_backColor, 0, _canvasOffset.Y, canvas.Width, canvas.Height * _zoom);
@@ -281,21 +340,19 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			//center marker
 			g.DrawLine(_penBoundary, canvas.Width / 2 + _canvasOffset.X, 0, canvas.Width / 2 + _canvasOffset.X, canvas.Height);
 
-			//draw the pose
+			//draw the data
 			List<string> markers = _ignoreMarkers ? null : _markers;
-			foreach (LiveSprite sprite in _pose.DrawingOrder)
+			LiveObject preview = _selectedPreview;
+			if (preview != null && (!preview.IsVisible || (!_recording && Playing)))
 			{
-				sprite.Draw(g, SceneTransform, markers);
-				if (_selectionSource == sprite && _selectedObject != null && _selectedObject.IsVisible && !_selectionSource.Hidden && (_recording || !_playing))
-				{
-					_selectedObject.Draw(g, SceneTransform, markers);
-				}
+				preview = null;
 			}
+			_data.Draw(g, SceneTransform, markers, _selectionSource, preview, Playing);
 
 			//selection and gizmos
-			if (_selectedObject != null && _selectedObject.IsVisible && !_selectionSource.Hidden && (_recording || !_playing))
+			if (_selectionSource != null && _selectedPreview != null && _selectedPreview.IsVisible && !_selectionSource.Hidden && (_recording || !Playing))
 			{
-				DrawSelection(g, _selectedObject);
+				DrawSelection(g);
 
 				//rotation arrow
 				if (_moveContext == HoverContext.Rotate)
@@ -304,7 +361,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 					Point pt = new Point(_lastMouse.X - arrow.Width / 2, _lastMouse.Y - arrow.Height / 2);
 
 					//rotate to face the object's pivot
-					PointF center = _selectedObject.ToScreenPt(_selectedObject.PivotX * _selectedObject.Width, _selectedObject.PivotY * _selectedObject.Height, SceneTransform);
+					PointF center = _selectedPreview.ToScreenPt(_selectedPreview.PivotX * _selectedPreview.Width, _selectedPreview.PivotY * _selectedPreview.Height, SceneTransform);
 
 					double angle = Math.Atan2(center.Y - _lastMouse.Y, center.X - _lastMouse.X);
 					angle = angle * (180 / Math.PI) - 90;
@@ -318,61 +375,17 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			}
 		}
 
-		private void DrawSelection(Graphics g, LiveSprite sprite)
+		private void DrawSelection(Graphics g)
 		{
-			if (_selectionSource != null && _selectionSource.Hidden)
+			LiveObject selection = _selectedPreview;
+			if (selection != null && _selectionSource != null && _selectionSource.Hidden)
 			{
 				return;
 			}
 
-			int midX = sprite.Width / 2;
-			int midY = sprite.Height / 2;
-			PointF[] localPts = new PointF[] {
-				new PointF(0,0),
-				new PointF(sprite.Width, 0),
-				new PointF(sprite.Width, sprite.Height),
-				new PointF(0, sprite.Height),
-				new PointF(midX, midY), //index 4: center
-				new PointF(midX, 0), //index 5: top handle
-				new PointF(sprite.Width, midY), //index 6: right handle
-				new PointF(midX, sprite.Height), //index 7: bottom handle
-				new PointF(0, midY), //index 8: left handle
-				new PointF(sprite.PivotX * sprite.Width, sprite.PivotY * sprite.Height), //index 9: pivot
-			};
-			PointF[] boundPts = sprite.ToScreenPt(SceneTransform, localPts);
-			PointF[] outerPts = new PointF[4];
-			for (int i = 0; i < 4; i++)
-			{
-				PointF boundPt = boundPts[i];
-				outerPts[i] = boundPt;
-			}
-
-			g.DrawPolygon(_penOuterSelection, outerPts);
-			g.DrawPolygon(_penInnerSelection, outerPts);
-
-
-			//Grab handles
-			for (int i = 5; i <= 8; i++)
-			{
-				PointF handle = boundPts[i];
-				g.FillRectangle(_brushHandle, handle.X - 3, handle.Y - 3, 6, 6);
-			}
-
-			//pivot point
-			if (_state == CanvasState.MovingPivot || _moveContext == HoverContext.Pivot)
-			{
-				g.MultiplyTransform(sprite.UnscaledWorldTransform);
-				g.MultiplyTransform(SceneTransform, MatrixOrder.Append);
-				g.DrawRectangle(_penKeyframe, 0, 0, sprite.Width, sprite.Height);
-				g.ResetTransform();
-			}
-
-			PointF pt = localPts[9];
-			g.FillEllipse(Brushes.White, pt.X - 3, pt.Y - 3, 6, 6);
-			g.FillEllipse(Brushes.Black, pt.X - 2, pt.Y - 2, 4, 4);
+			selection.DrawSelection(g, SceneTransform, _state, _moveContext);
 		}
 
-
 		private void Canvas_KeyDown(object sender, KeyEventArgs e)
 		{
 			if (e.KeyCode == Keys.Down)
@@ -395,12 +408,12 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		public void MoveSelectedObject(int x, int y)
 		{
-			if (_selectedObject != null)
+			if (_selectedPreview != null)
 			{
-				PointF worldPt = _selectedObject.ToWorldPt(_selectedObject.X, _selectedObject.Y);
+				PointF worldPt = _selectedPreview.ToWorldPt(_selectedPreview.X, _selectedPreview.Y);
 				worldPt.X += x;
 				worldPt.Y += y;
-				_selectedObject.SetWorldPosition(worldPt);
+				_selectedPreview.SetWorldPosition(worldPt, SceneTransform);
 				canvas.Invalidate();
 				canvas.Update();
 			}
@@ -411,64 +424,45 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			_downPoint = new Point(e.X, e.Y);
 			if (e.Button == MouseButtons.Left)
 			{
-				if (_playing && !_recording) { return; }
+				CanvasClicked?.Invoke(this, EventArgs.Empty);
+				if (DisallowEdit || (Playing && !_recording)) { return; }
 				//object selection
-				LiveSprite obj = null;
+				LiveObject obj = null;
 				if (_moveContext == HoverContext.None || _moveContext == HoverContext.Select)
 				{
-					//Sprite
+					//object
 					if (obj == null)
 					{
-						obj = GetObjectAtPoint(e.X, e.Y, _pose.DrawingOrder);
+						obj = _data.GetObjectAtPoint(e.X, e.Y, SceneTransform, _ignoreMarkers, _markers);
 					}
 
-					if (obj != null && _selectedObject != obj)
+					if (obj != null && _selectedPreview != obj)
 					{
 						SelectObject(obj);
 					}
-					//if (obj == null && _selectedObject != null)
-					//{
-					//	PointF worldPt = _selectedObject.ScreenToWorldPt(SceneTransform, _downPoint)[0];
-					//	SimpleIK ik = new SimpleIK();
-					//	ik.Solve(_selectedObject, worldPt, SceneTransform);
-					//}
+				}
+				switch (_moveContext)
+				{
+					case HoverContext.ArrowLeft:
+					case HoverContext.ArrowDown:
+					case HoverContext.ArrowRight:
+					case HoverContext.ArrowUp:
+						UpdateArrowPosition();
+						break;
 				}
 			}
 			else if (e.Button == MouseButtons.Right)
 			{
-				_lastMouse = new Point(e.X, e.Y);
-				_state = CanvasState.Panning;
-				canvas.Cursor = Cursors.NoMove2D;
-			}
-		}
-
-		/// <summary>
-		/// Gets the topmost object beneath the given screen coordinate
-		/// </summary>
-		/// <param name="x"></param>
-		/// <param name="y"></param>
-		/// <param name="objects"></param>
-		/// <returns></returns>
-		private LiveSprite GetObjectAtPoint(int x, int y, List<LiveSprite> objects)
-		{
-			//search in reverse order because objects are sorted by depth
-			for (int i = objects.Count - 1; i >= 0; i--)
-			{
-				LiveSprite obj = objects[i];
-				if (!obj.IsVisible || obj.Hidden || obj.Alpha == 0 || obj.HiddenByMarker(_ignoreMarkers ? null : _markers)) { continue; }
-				
-				//transform point to local space
-				PointF localPt = obj.ToLocalPt(x, y, SceneTransform);
-				if (localPt.X >= 0 && localPt.X <= obj.Width &&
-					localPt.Y >= 0 && localPt.Y <= obj.Height)
+				if (_viewport == null || _viewport.AllowPan)
 				{
-					return obj;
+					_lastMouse = new Point(e.X, e.Y);
+					_state = CanvasState.Panning;
+					canvas.Cursor = Cursors.NoMove2D;
 				}
 			}
-			return null;
 		}
 
-		private void SelectObject(LiveSprite obj)
+		private void SelectObject(LiveObject obj)
 		{
 			ObjectSelected?.Invoke(this, new CanvasSelectionArgs(obj, ModifierKeys));
 		}
@@ -479,47 +473,48 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		/// <param name="worldPt"></param>
 		private HoverContext GetContext(PointF screenPt)
 		{
-			LiveSprite objAtPoint = GetObjectAtPoint((int)screenPt.X, (int)screenPt.Y, _pose.DrawingOrder);
-			if (_selectedObject != null)
+			LiveObject objAtPoint = _data.GetObjectAtPoint((int)screenPt.X, (int)screenPt.Y, SceneTransform, _ignoreMarkers, _markers);
+			if (_selectedPreview != null)
 			{
-				List<LiveSprite> selection = new List<LiveSprite>();
-				selection.Add(_selectedObject);
-				LiveSprite selected = GetObjectAtPoint((int)screenPt.X, (int)screenPt.Y, selection);
+				List<LiveObject> selection = new List<LiveObject>();
+				selection.Add(_selectedPreview);
+				LiveObject selected = _data.GetObjectAtPoint((int)screenPt.X, (int)screenPt.Y, SceneTransform, _ignoreMarkers, _markers);
 				if (selected != null)
 				{
 					objAtPoint = selected;
 				}
 			}
 
-			if (_selectedObject != null && !_selectionSource.Hidden)
+			if (_selectedPreview != null && !_selectionSource.Hidden)
 			{
 				bool locked = false;
 
 				//convert the screen pt to the selected object's local space
-				PointF pt = _selectedObject.ToLocalPt(SceneTransform, screenPt)[0];
+				PointF pt = _selectedPreview.ToLocalPt(SceneTransform, screenPt)[0];
 				Point localPt = new Point(0, 0);
 				localPt.X = (int)Math.Round(pt.X);
 				localPt.Y = (int)Math.Round(pt.Y);
 
 				PointF[] corners = new PointF[] {
 					new PointF(0, 0),
-					new PointF(_selectedObject.Width, 0),
-					new PointF(_selectedObject.Width, _selectedObject.Height),
-					new PointF(0, _selectedObject.Height),
-					new PointF(_selectedObject.PivotX * _selectedObject.Width, _selectedObject.PivotY * _selectedObject.Height),
+					new PointF(_selectedPreview.Width, 0),
+					new PointF(_selectedPreview.Width, _selectedPreview.Height),
+					new PointF(0, _selectedPreview.Height),
+					new PointF(_selectedPreview.PivotX * _selectedPreview.Width, _selectedPreview.PivotY * _selectedPreview.Height),
 				};
-				_selectedObject.ToScreenPt(SceneTransform, corners);
+				_selectedPreview.ToScreenPt(SceneTransform, corners);
 				PointF tl = corners[0];
 				PointF tr = corners[1];
 				PointF br = corners[2];
 				PointF bl = corners[3];
 
-				bool visible = _selectedObject.IsVisible;
-				bool allowTranslate = visible;
-				bool allowPivot = visible;
-				bool allowRotate = visible;
-				bool allowScale = visible;
-				bool allowSkew = visible;
+				bool visible = _selectedPreview.IsVisible;
+				bool allowTranslate = visible && _selectedPreview.CanTranslate;
+				bool allowPivot = visible && _selectedPreview.CanPivot;
+				bool allowRotate = visible && _selectedPreview.CanRotate;
+				bool allowScale = visible && _selectedPreview.CanScale;
+				bool allowSkew = visible && _selectedPreview.CanSkew;
+				bool allowArrow = visible && _selectedPreview.CanArrow;
 
 				float dl = screenPt.DistanceFromLineSegment(bl, tl);
 				float dr = screenPt.DistanceFromLineSegment(br, tr);
@@ -545,12 +540,12 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 					//rotating - hovering outside a corner
 					if (localPt.X < -SelectionLeeway && localPt.X >= -RotationLeeway && dt <= RotationLeeway ||
 						localPt.Y < -SelectionLeeway && localPt.Y >= -RotationLeeway && dl <= RotationLeeway ||
-						localPt.X > _selectedObject.Width + SelectionLeeway && localPt.X <= _selectedObject.Width + RotationLeeway && dt <= RotationLeeway ||
+						localPt.X > _selectedPreview.Width + SelectionLeeway && localPt.X <= _selectedPreview.Width + RotationLeeway && dt <= RotationLeeway ||
 						localPt.Y < -SelectionLeeway && localPt.Y >= -RotationLeeway && dr <= RotationLeeway ||
 						localPt.X < -SelectionLeeway && localPt.X >= -RotationLeeway && db <= RotationLeeway ||
-						localPt.Y > _selectedObject.Height + SelectionLeeway && localPt.Y <= _selectedObject.Height + RotationLeeway && dl <= RotationLeeway ||
-						localPt.X > _selectedObject.Width + SelectionLeeway && localPt.X <= _selectedObject.Width + RotationLeeway && db <= RotationLeeway ||
-						localPt.Y > _selectedObject.Height + SelectionLeeway && localPt.Y <= _selectedObject.Height + RotationLeeway && dr <= RotationLeeway)
+						localPt.Y > _selectedPreview.Height + SelectionLeeway && localPt.Y <= _selectedPreview.Height + RotationLeeway && dl <= RotationLeeway ||
+						localPt.X > _selectedPreview.Width + SelectionLeeway && localPt.X <= _selectedPreview.Width + RotationLeeway && db <= RotationLeeway ||
+						localPt.Y > _selectedPreview.Height + SelectionLeeway && localPt.Y <= _selectedPreview.Height + RotationLeeway && dr <= RotationLeeway)
 					{
 						return locked ? HoverContext.Locked : HoverContext.Rotate;
 					}
@@ -559,7 +554,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				if (allowSkew && ModifierKeys.HasFlag(Keys.Shift))
 				{
 					//skewing - grabbing an edge while Shift is held down
-					if (0 <= localPt.Y && localPt.Y <= _selectedObject.Height)
+					if (0 <= localPt.Y && localPt.Y <= _selectedPreview.Height)
 					{
 						if (dl <= SelectionLeeway)
 						{
@@ -570,7 +565,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 							return locked ? HoverContext.Locked : HoverContext.SkewRight;
 						}
 					}
-					if (0 <= localPt.X && localPt.X <= _selectedObject.Width)
+					if (0 <= localPt.X && localPt.X <= _selectedPreview.Width)
 					{
 						if (dt <= SelectionLeeway)
 						{
@@ -583,6 +578,32 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 					}
 				}
 
+				if (allowArrow)
+				{
+					if (tr.Y <= screenPt.Y && screenPt.Y <= br.Y)
+					{
+						if (dl > SelectionLeeway && screenPt.X < tl.X && dl <= RotationLeeway)
+						{
+							return locked ? HoverContext.Locked : HoverContext.ArrowLeft;
+						}
+						else if (dr <= RotationLeeway && dr > SelectionLeeway && screenPt.X > tr.X)
+						{
+							return locked ? HoverContext.Locked : HoverContext.ArrowRight;
+						}
+					}
+					if (tl.X <= screenPt.X && screenPt.X <= tr.X)
+					{
+						if (screenPt.Y < tl.Y && dt <= RotationLeeway)
+						{
+							return locked ? HoverContext.Locked : HoverContext.ArrowUp;
+						}
+						else if (screenPt.Y >= br.Y && db <= RotationLeeway)
+						{
+							return locked ? HoverContext.Locked : HoverContext.ArrowDown;
+						}
+					}
+				}
+
 				if (allowScale)
 				{
 					//scaling/stretching - grabbing an edge
@@ -596,7 +617,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 						{
 							return locked ? HoverContext.Locked : HoverContext.ScaleBottom | HoverContext.ScaleLeft;
 						}
-						else if (0 <= localPt.Y && localPt.Y <= _selectedObject.Height)
+						else if (0 <= localPt.Y && localPt.Y <= _selectedPreview.Height)
 						{
 							return locked ? HoverContext.Locked : HoverContext.ScaleLeft;
 						}
@@ -612,24 +633,24 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 						{
 							return locked ? HoverContext.Locked : HoverContext.ScaleBottom | HoverContext.ScaleRight;
 						}
-						else if (0 <= localPt.Y && localPt.Y <= _selectedObject.Height)
+						else if (0 <= localPt.Y && localPt.Y <= _selectedPreview.Height)
 						{
 							return locked ? HoverContext.Locked : HoverContext.ScaleRight;
 						}
 					}
 
-					if (dt <= SelectionLeeway && 0 <= localPt.X && localPt.X <= _selectedObject.Width)
+					if (dt <= SelectionLeeway && 0 <= localPt.X && localPt.X <= _selectedPreview.Width)
 					{
 						return locked ? HoverContext.Locked : HoverContext.ScaleTop;
 					}
 
-					if (db <= SelectionLeeway && 0 <= localPt.X && localPt.X <= _selectedObject.Width)
+					if (db <= SelectionLeeway && 0 <= localPt.X && localPt.X <= _selectedPreview.Width)
 					{
 						return locked ? HoverContext.Locked : HoverContext.ScaleBottom;
 					}
 				}
 
-				if (objAtPoint != null && objAtPoint != _selectedObject && objAtPoint != _selectionSource)
+				if (objAtPoint != null && objAtPoint != _selectedPreview && objAtPoint != _selectionSource)
 				{
 					//selecting another object takes priority over translation but nothing else
 					return HoverContext.Select;
@@ -637,15 +658,15 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 				if (allowTranslate)
 				{
-					if (0 <= localPt.X && localPt.X <= _selectedObject.Width &&
-						0 <= localPt.Y && localPt.Y <= _selectedObject.Height)
+					if (0 <= localPt.X && localPt.X <= _selectedPreview.Width &&
+						0 <= localPt.Y && localPt.Y <= _selectedPreview.Height)
 					{
 						return HoverContext.Drag;
 					}
 				}
 			}
 
-			if (objAtPoint != null && objAtPoint != _selectedObject && objAtPoint != _selectionSource)
+			if (objAtPoint != null && objAtPoint != _selectedPreview && objAtPoint != _selectionSource)
 			{
 				return HoverContext.Select;
 			}
@@ -655,6 +676,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		private void canvas_MouseMove(object sender, MouseEventArgs e)
 		{
+			if (DisallowEdit) { return; }
 			Point screenPt = new Point(e.X, e.Y);
 
 			switch (_state)
@@ -699,7 +721,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		private void SetContext(MouseEventArgs e, Point screenPt)
 		{
 			HoverContext context = GetContext(screenPt);
-			if (_playing && !_recording && context != HoverContext.CameraPan)
+			if (Playing && !_recording && context != HoverContext.CameraPan)
 			{
 				context = HoverContext.None;
 			}
@@ -719,8 +741,8 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 					switch (context)
 					{
 						case HoverContext.Drag:
-							_startDragPosition = new Point((int)_selectedObject.X, (int)_selectedObject.Y);
-							_startDragPosition = _selectedObject.ToWorldPt(_startDragPosition)[0];
+							_startDragPosition = new Point((int)_selectedPreview.X, (int)_selectedPreview.Y);
+							_startDragPosition = _selectedPreview.ToWorldPt(_startDragPosition)[0];
 							_state = CanvasState.MovingObject;
 							break;
 						case HoverContext.ScaleTopLeft:
@@ -732,11 +754,11 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 						case HoverContext.ScaleRight:
 						case HoverContext.ScaleBottom:
 							_moveContext = context;
-							Cursor = Timeline.HandClosed;
+							Cursor = Cursors.Hand;
 							_state = CanvasState.Scaling;
 							break;
 						case HoverContext.Rotate:
-							_startDragRotation = _selectedObject.Rotation;
+							_startDragRotation = _selectedPreview.Rotation;
 							_state = CanvasState.Rotating;
 							break;
 						case HoverContext.Pivot:
@@ -765,7 +787,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				case HoverContext.ScaleRight:
 				case HoverContext.ScaleTop:
 				case HoverContext.ScaleBottom:
-					canvas.Cursor = Timeline.HandOpen;
+					canvas.Cursor = Cursors.Hand;
 					break;
 				case HoverContext.Drag:
 					canvas.Cursor = Cursors.SizeAll;
@@ -855,63 +877,112 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			};
 
 			//convert to world space
-			pts = _selectedObject.ScreenToWorldPt(SceneTransform, pts);
+			pts = _selectedPreview.ScreenToWorldPt(SceneTransform, pts);
 			float x = pts[1].X - pts[0].X + _startDragPosition.X;
 			float y = pts[1].Y - pts[0].Y + _startDragPosition.Y;
 
-			_selectedObject.SetWorldPosition(new PointF(x, y));
+			_selectedPreview.SetWorldPosition(new PointF(x, y), SceneTransform);
 			canvas.Invalidate();
 			canvas.Update();
 		}
 
 		/// <summary>
-		/// Moves a sprite's pivot point
+		/// Moves an object's pivot point
 		/// </summary>
 		/// <param name="screenPt"></param>
 		private void MovePivot(Point screenPt)
 		{
-			_selectedObject.AdjustPivot(screenPt, SceneTransform);
+			_selectedPreview.AdjustPivot(screenPt, SceneTransform);
 			canvas.Invalidate();
 			canvas.Update();
 		}
 
 		private void ScaleObject(Point screenPt)
 		{
-			_selectedObject.Scale(screenPt, SceneTransform, _moveContext);
+			_selectedPreview.Scale(screenPt, SceneTransform, _moveContext);
 			canvas.Invalidate();
 			canvas.Update();
 		}
 
 		private void RotateObject(Point screenPt)
 		{
-			PointF pivot = _selectedObject.ToScreenPt(_selectedObject.PivotX * _selectedObject.Width, _selectedObject.PivotY * _selectedObject.Height, SceneTransform);
-			_selectedObject.Rotate(screenPt, pivot, _downPoint, _startDragRotation);
+			PointF pivot = _selectedPreview.ToScreenPt(_selectedPreview.PivotX * _selectedPreview.Width, _selectedPreview.PivotY * _selectedPreview.Height, SceneTransform);
+			_selectedPreview.Rotate(screenPt, pivot, _downPoint, _startDragRotation);
 			canvas.Invalidate();
 			canvas.Update();
 		}
 
 		private void SkewObject(Point screenPt)
 		{
-			_selectedObject.Skew(screenPt, _downPoint, _moveContext, _zoom);
+			_selectedPreview.Skew(screenPt, _downPoint, _moveContext, _zoom);
 			canvas.Invalidate();
 			canvas.Update();
 		}
 
+		private void UpdateArrowPosition()
+		{
+			_selectedPreview.UpdateArrowPosition(_moveContext);
+		}
+
 		private void cmdMarkers_Click(object sender, EventArgs e)
 		{
 			MarkerSetup form = new MarkerSetup();
-			form.SetData(_character.Character, _markers);
+			form.SetData(_character.Character, _userMarkers);
 			if (form.ShowDialog() == DialogResult.OK)
 			{
-				_markers = form.Markers;
+				_userMarkers = form.Markers;
+				_markers.Clear();
+				_markers.AddRange(_userMarkers);
+				foreach (string marker in _removedMarkers)
+				{
+					_markers.Remove(marker);
+				}
+				foreach (string marker in _addedMarkers)
+				{
+					if (!_markers.Contains(marker))
+					{
+						_markers.Add(marker);
+					}
+				}
+
 				canvas.Invalidate();
 			}
 		}
 
 		private void cmdFit_Click(object sender, EventArgs e)
 		{
-			_canvasOffset = new Point(0, 0);
-			UpdateZoomIndex(DefaultZoomIndex);
+			FitScreen();
+		}
+
+		public void FitScreen()
+		{
+			if (_data == null) { return; }
+			_data.FitScene(canvas.Width, canvas.Height, ref _canvasOffset, ref _zoom);
+			//find the most appropriate index for the new zoom
+			int closestIndex = 0;
+			for (int i = 0; i < _zoomLevels.Length; i++)
+			{
+				float diff = Math.Abs(_zoomLevels[i] - _zoom);
+				if (diff < 0.001f)
+				{
+					UpdateZoomIndex(i);
+					return;
+				}
+				else if (_zoomLevels[i] < _zoom)
+				{
+					closestIndex = i;
+				}
+				else
+				{
+					break;
+				}
+			}
+			_zoomIndex = closestIndex;
+			tsZoomIn.Enabled = AllowZoom && _zoomIndex < _zoomLevels.Length - 1;
+			tsZoomOut.Enabled = AllowZoom && _zoomIndex > 0;
+			tsZoom.Enabled = AllowZoom;
+			tsZoom.Text = $"{_zoom}x";
+			UpdateSceneTransform();
 			canvas.Invalidate();
 		}
 
@@ -946,14 +1017,17 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		{
 			if (ModifierKeys.HasFlag(Keys.Control))
 			{
-				((HandledMouseEventArgs)e).Handled = true;
-				if (e.Delta > 0)
+				if (AllowZoom)
 				{
-					UpdateZoomIndex(_zoomIndex + 1);
-				}
-				else if (e.Delta < 0)
-				{
-					UpdateZoomIndex(_zoomIndex - 1);
+					((HandledMouseEventArgs)e).Handled = true;
+					if (e.Delta > 0)
+					{
+						UpdateZoomIndex(_zoomIndex + 1);
+					}
+					else if (e.Delta < 0)
+					{
+						UpdateZoomIndex(_zoomIndex - 1);
+					}
 				}
 			}
 		}
@@ -986,9 +1060,30 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			if (colorDialog1.ShowDialog() == DialogResult.OK)
 			{
 				_backColor.Color = colorDialog1.Color;
+				_backColorCustomized = true;
 				canvas.Invalidate();
 			}
 		}
+
+		public void InvalidateCanvas()
+		{
+			canvas.Invalidate();
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			if (!_backColorCustomized)
+			{
+				if (skin.Group == "Dark")
+				{
+					_backColor.Color = Color.FromArgb(50, 50, 50);
+				}
+				else
+				{
+					_backColor.Color = Color.LightGray;
+				}
+			}
+		}
 	}
 
 	public class CanvasSelectionArgs : EventArgs
@@ -1002,4 +1097,18 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			Modifiers = modifiers;
 		}
 	}
+
+	public class CanvasPaintArgs : EventArgs
+	{
+		public Graphics Graphics { get; private set; }
+		public int Width { get; private set; }
+		public int Height { get; private set; }
+
+		public CanvasPaintArgs(Graphics g, int width, int height)
+		{
+			Graphics = g;
+			Width = width;
+			Height = height;
+		}
+	}
 }
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/LiveImageCache.cs b/editor source/SPNATI Character Editor/EpilogueEditing/LiveImageCache.cs
index 5b56a52aedabf2e73571369215a807b520988c49..ab570d75be0c36632bc2b0ef51b2fc6f4d39dce3 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/LiveImageCache.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/LiveImageCache.cs	
@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using System.Drawing;
 using System.IO;
+using System.Linq;
 
 namespace SPNATI_Character_Editor
 {
@@ -17,6 +18,16 @@ namespace SPNATI_Character_Editor
 			_images.Clear();
 		}
 
+		public static void Refresh()
+		{
+			string[] keys = _images.Keys.ToArray();
+			foreach (string key in keys)
+			{
+				_images.Remove(key);
+				Get(key);
+			}
+		}
+
 		public static Bitmap Get(string src)
 		{
 			if (string.IsNullOrEmpty(src)) { return null; }
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/PoseEditor.Designer.cs b/editor source/SPNATI Character Editor/EpilogueEditing/PoseEditor.Designer.cs
index c8759c2435a2a89004a76bb01cff2ec6222709b6..3179ec85da3f7190e40aa2271353f0b2a93af189 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/PoseEditor.Designer.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/PoseEditor.Designer.cs	
@@ -29,16 +29,20 @@
 		private void InitializeComponent()
 		{
 			this.components = new System.ComponentModel.Container();
-			this.splitContainer1 = new System.Windows.Forms.SplitContainer();
-			this.splitContainer2 = new System.Windows.Forms.SplitContainer();
+			this.splitContainer1 = new Desktop.Skinning.SkinnedSplitContainer();
+			this.splitContainer2 = new Desktop.Skinning.SkinnedSplitContainer();
 			this.tsMainMenu = new System.Windows.Forms.ToolStrip();
 			this.tsAddSprite = new System.Windows.Forms.ToolStripButton();
 			this.tsRemoveSprite = new System.Windows.Forms.ToolStripButton();
 			this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
+			this.tsRefresh = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
 			this.tsAddKeyframe = new System.Windows.Forms.ToolStripButton();
 			this.tsRemoveKeyframe = new System.Windows.Forms.ToolStripButton();
 			this.tsAddEndFrame = new System.Windows.Forms.ToolStripButton();
-			this.timeline = new SPNATI_Character_Editor.EpilogueEditor.Timeline();
+			this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
+			this.tsCreateSequence = new System.Windows.Forms.ToolStripButton();
+			this.tsFrameType = new System.Windows.Forms.ToolStripButton();
 			this.splitContainer3 = new System.Windows.Forms.SplitContainer();
 			this.lstPoses = new Desktop.CommonControls.RefreshableListBox();
 			this.tsPoses = new System.Windows.Forms.ToolStrip();
@@ -49,12 +53,12 @@
 			this.tsCopy = new System.Windows.Forms.ToolStripButton();
 			this.tsPaste = new System.Windows.Forms.ToolStripButton();
 			this.tsDuplicate = new System.Windows.Forms.ToolStripButton();
-			this.lblDataCaption = new System.Windows.Forms.Label();
+			this.tsExport = new System.Windows.Forms.ToolStripButton();
+			this.lblDataCaption = new Desktop.Skinning.SkinnedLabel();
 			this.table = new Desktop.CommonControls.PropertyTable();
+			this.timeline = new SPNATI_Character_Editor.EpilogueEditor.Timeline();
 			this.canvas = new SPNATI_Character_Editor.EpilogueEditor.LiveCanvas();
 			this.openFileDialog1 = new SPNATI_Character_Editor.Controls.CharacterImageDialog();
-			this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
-			this.tsCreateSequence = new System.Windows.Forms.ToolStripButton();
 			((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
 			this.splitContainer1.Panel1.SuspendLayout();
 			this.splitContainer1.Panel2.SuspendLayout();
@@ -115,10 +119,13 @@
             this.tsAddSprite,
             this.tsRemoveSprite,
             this.toolStripSeparator2,
+            this.tsRefresh,
+            this.toolStripSeparator4,
             this.tsAddKeyframe,
             this.tsRemoveKeyframe,
             this.tsAddEndFrame,
-            this.toolStripSeparator3,
+			this.tsFrameType,
+			this.toolStripSeparator3,
             this.tsCreateSequence});
 			this.tsMainMenu.Location = new System.Drawing.Point(0, 0);
 			this.tsMainMenu.Name = "tsMainMenu";
@@ -151,6 +158,22 @@
 			this.toolStripSeparator2.Name = "toolStripSeparator2";
 			this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
 			// 
+			// tsRefresh
+			// 
+			this.tsRefresh.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsRefresh.Image = global::SPNATI_Character_Editor.Properties.Resources.Refresh;
+			this.tsRefresh.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsRefresh.Name = "tsRefresh";
+			this.tsRefresh.Size = new System.Drawing.Size(23, 22);
+			this.tsRefresh.Text = "Refresh assets";
+			this.tsRefresh.ToolTipText = "Reload sprites from files";
+			this.tsRefresh.Click += new System.EventHandler(this.tsRefresh_Click);
+			// 
+			// toolStripSeparator4
+			// 
+			this.toolStripSeparator4.Name = "toolStripSeparator4";
+			this.toolStripSeparator4.Size = new System.Drawing.Size(6, 25);
+			// 
 			// tsAddKeyframe
 			// 
 			this.tsAddKeyframe.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
@@ -181,20 +204,31 @@
 			this.tsAddEndFrame.Text = "Copy first keyframe to end";
 			this.tsAddEndFrame.Click += new System.EventHandler(this.CopyFirstFrame_Click);
 			// 
-			// timeline
+			// toolStripSeparator3
 			// 
-			this.timeline.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.timeline.CommandHistory = null;
-			this.timeline.CurrentTime = 0F;
-			this.timeline.Enabled = false;
-			this.timeline.Location = new System.Drawing.Point(0, 25);
-			this.timeline.Margin = new System.Windows.Forms.Padding(0);
-			this.timeline.Name = "timeline";
-			this.timeline.PlaybackTime = 0F;
-			this.timeline.Size = new System.Drawing.Size(503, 374);
-			this.timeline.TabIndex = 1;
+			this.toolStripSeparator3.Name = "toolStripSeparator3";
+			this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
+			// 
+			// tsCreateSequence
+			// 
+			this.tsCreateSequence.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsCreateSequence.Image = global::SPNATI_Character_Editor.Properties.Resources.AddTransition;
+			this.tsCreateSequence.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsCreateSequence.Name = "tsCreateSequence";
+			this.tsCreateSequence.Size = new System.Drawing.Size(23, 22);
+			this.tsCreateSequence.Text = "Create sequence";
+			this.tsCreateSequence.Click += new System.EventHandler(this.tsCreateSequence_Click);
+			// 
+			// tsFrameType
+			// 
+			this.tsFrameType.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsFrameType.Image = global::SPNATI_Character_Editor.Properties.Resources.SplitKeyframe;
+			this.tsFrameType.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsFrameType.Name = "tsFrameType";
+			this.tsFrameType.Size = new System.Drawing.Size(23, 22);
+			this.tsFrameType.Text = "Toggle keyframe type";
+			this.tsFrameType.Click += new System.EventHandler(this.tsFrameType_Click);
+
 			// 
 			// splitContainer3
 			// 
@@ -217,7 +251,10 @@
 			// 
 			// lstPoses
 			// 
+			this.lstPoses.BackColor = System.Drawing.Color.White;
 			this.lstPoses.Dock = System.Windows.Forms.DockStyle.Fill;
+			this.lstPoses.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstPoses.ForeColor = System.Drawing.Color.Black;
 			this.lstPoses.FormattingEnabled = true;
 			this.lstPoses.Location = new System.Drawing.Point(0, 25);
 			this.lstPoses.Name = "lstPoses";
@@ -236,11 +273,13 @@
             this.tsCut,
             this.tsCopy,
             this.tsPaste,
-            this.tsDuplicate});
+            this.tsDuplicate,
+            this.tsExport});
 			this.tsPoses.Location = new System.Drawing.Point(0, 0);
 			this.tsPoses.Name = "tsPoses";
 			this.tsPoses.Size = new System.Drawing.Size(167, 25);
 			this.tsPoses.TabIndex = 0;
+			this.tsPoses.Tag = "Surface";
 			// 
 			// tsAddPose
 			// 
@@ -307,11 +346,24 @@
 			this.tsDuplicate.Text = "Duplicate Pose";
 			this.tsDuplicate.Click += new System.EventHandler(this.tsDuplicate_Click);
 			// 
+			// tsExport
+			// 
+			this.tsExport.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+			this.tsExport.Image = global::SPNATI_Character_Editor.Properties.Resources.GIF;
+			this.tsExport.ImageTransparentColor = System.Drawing.Color.Magenta;
+			this.tsExport.Name = "tsExport";
+			this.tsExport.Size = new System.Drawing.Size(23, 20);
+			this.tsExport.Text = "Export Pose as GIF";
+			this.tsExport.Click += new System.EventHandler(this.tsExport_Click);
+			// 
 			// lblDataCaption
 			// 
 			this.lblDataCaption.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblDataCaption.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.lblDataCaption.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.lblDataCaption.ForeColor = System.Drawing.Color.Blue;
+			this.lblDataCaption.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblDataCaption.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
 			this.lblDataCaption.Location = new System.Drawing.Point(-1, -1);
 			this.lblDataCaption.Name = "lblDataCaption";
 			this.lblDataCaption.Size = new System.Drawing.Size(334, 26);
@@ -328,26 +380,48 @@
 			this.table.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.table.BackColor = System.Drawing.Color.White;
 			this.table.Data = null;
 			this.table.Enabled = false;
+			this.table.HeaderType = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.table.HideAddField = true;
 			this.table.HideSpeedButtons = true;
-			this.table.Location = new System.Drawing.Point(0, 25);
+			this.table.Location = new System.Drawing.Point(-1, 25);
+			this.table.ModifyingProperty = null;
 			this.table.Name = "table";
+			this.table.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.table.PlaceholderText = null;
 			this.table.PreserveControls = true;
 			this.table.PreviewData = null;
 			this.table.RemoveCaption = "Remove";
 			this.table.RowHeaderWidth = 0F;
 			this.table.RunInitialAddEvents = false;
-			this.table.Size = new System.Drawing.Size(332, 259);
+			this.table.Size = new System.Drawing.Size(333, 259);
 			this.table.Sorted = true;
 			this.table.TabIndex = 5;
 			this.table.UndoManager = null;
 			this.table.UseAutoComplete = true;
 			// 
+			// timeline
+			// 
+			this.timeline.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+			| System.Windows.Forms.AnchorStyles.Left)
+			| System.Windows.Forms.AnchorStyles.Right)));
+			this.timeline.CommandHistory = null;
+			this.timeline.CurrentTime = 0F;
+			this.timeline.ElapsedTime = 0F;
+			this.timeline.Enabled = false;
+			this.timeline.Location = new System.Drawing.Point(0, 25);
+			this.timeline.Margin = new System.Windows.Forms.Padding(0);
+			this.timeline.Name = "timeline";
+			this.timeline.PlaybackAwaitingInput = false;
+			this.timeline.PlaybackTime = 0F;
+			this.timeline.Size = new System.Drawing.Size(503, 374);
+			this.timeline.TabIndex = 1;
+			// 
 			// canvas
 			// 
+			this.canvas.AllowZoom = true;
 			this.canvas.Dock = System.Windows.Forms.DockStyle.Fill;
 			this.canvas.Enabled = false;
 			this.canvas.Location = new System.Drawing.Point(0, 0);
@@ -362,21 +436,6 @@
 			this.openFileDialog1.IncludeOpponents = false;
 			this.openFileDialog1.UseAbsolutePaths = false;
 			// 
-			// toolStripSeparator3
-			// 
-			this.toolStripSeparator3.Name = "toolStripSeparator3";
-			this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
-			// 
-			// tsCreateSequence
-			// 
-			this.tsCreateSequence.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
-			this.tsCreateSequence.Image = global::SPNATI_Character_Editor.Properties.Resources.AddTransition;
-			this.tsCreateSequence.ImageTransparentColor = System.Drawing.Color.Magenta;
-			this.tsCreateSequence.Name = "tsCreateSequence";
-			this.tsCreateSequence.Size = new System.Drawing.Size(23, 22);
-			this.tsCreateSequence.Text = "Create sequence";
-			this.tsCreateSequence.Click += new System.EventHandler(this.tsCreateSequence_Click);
-			// 
 			// PoseEditor
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -408,15 +467,15 @@
 
 		#endregion
 
-		private System.Windows.Forms.SplitContainer splitContainer1;
-		private System.Windows.Forms.SplitContainer splitContainer2;
+		private Desktop.Skinning.SkinnedSplitContainer splitContainer1;
+		private Desktop.Skinning.SkinnedSplitContainer splitContainer2;
 		private Desktop.CommonControls.PropertyTable table;
 		private Timeline timeline;
 		private System.Windows.Forms.SplitContainer splitContainer3;
 		private System.Windows.Forms.ToolStrip tsPoses;
 		private Desktop.CommonControls.RefreshableListBox lstPoses;
 		private LiveCanvas canvas;
-		private System.Windows.Forms.Label lblDataCaption;
+		private Desktop.Skinning.SkinnedLabel lblDataCaption;
 		private System.Windows.Forms.ToolStripButton tsAddPose;
 		private System.Windows.Forms.ToolStripButton tsRemovePose;
 		private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
@@ -434,5 +493,9 @@
 		private Controls.CharacterImageDialog openFileDialog1;
 		private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
 		private System.Windows.Forms.ToolStripButton tsCreateSequence;
+		private System.Windows.Forms.ToolStripButton tsRefresh;
+		private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
+		private System.Windows.Forms.ToolStripButton tsExport;
+		private System.Windows.Forms.ToolStripButton tsFrameType;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/PoseEditor.cs b/editor source/SPNATI Character Editor/EpilogueEditing/PoseEditor.cs
index 1b80e07da2f46756f472ce6deb56bc4d05f4583a..823f1f580c1f71ae33934f14ef03d9c3bf27d04b 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/PoseEditor.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/PoseEditor.cs	
@@ -19,6 +19,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		private Pose _sourcePose;
 		private UndoManager _history = new UndoManager();
 		private float _time;
+		private float _elapsedTime;
 		private float _playbackTime;
 		private bool _playing;
 		private ILabel _labelData;
@@ -32,8 +33,10 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			timeline.DataSelected += Timeline_DataSelected;
 			timeline.TimeChanged += Timeline_TimeChanged;
 			timeline.PlaybackTimeChanged += Timeline_PlaybackTimeChanged;
+			timeline.ElapsedTimeChanged += Timeline_ElapsedTimeChanged;
 			timeline.WidgetSelected += Timeline_WidgetSelected;
 			timeline.PlaybackChanged += Timeline_PlaybackChanged;
+			timeline.UIRequested += Timeline_UIRequested;
 			//table.UndoManager = _history;
 			table.RecordFilter = RecordFilter;
 
@@ -75,21 +78,27 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			timeline.SelectWidgetWithData(args.Object);
 		}
 
-		private void Timeline_WidgetSelected(object sender, ITimelineWidget widget)
+		private void Timeline_WidgetSelected(object sender, ITimelineObject widget)
 		{
 			canvas.SelectData(widget?.GetData());
 		}
 
+		private void Timeline_ElapsedTimeChanged(object sender, float time)
+		{
+			_elapsedTime = time;
+			canvas.UpdateTime(_time, _playbackTime, _elapsedTime);
+		}
+
 		private void Timeline_PlaybackTimeChanged(object sender, float time)
 		{
 			_playbackTime = time;
-			canvas.UpdateTime(_time, _playbackTime);
+			canvas.UpdateTime(_time, _playbackTime, _elapsedTime);
 		}
 
 		private void Timeline_TimeChanged(object sender, float time)
 		{
 			_time = time;
-			canvas.UpdateTime(time, _playbackTime);
+			canvas.UpdateTime(time, _playbackTime, _elapsedTime);
 			UpdateToolbar();
 		}
 
@@ -115,12 +124,6 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			SavePose();
 		}
 
-		public override void Destroy()
-		{
-			LiveImageCache.Clear();
-			base.Destroy();
-		}
-
 		private void RebuildPoseList()
 		{
 			lstPoses.Items.Clear();
@@ -287,12 +290,13 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		private void UpdateToolbar()
 		{
 			if (_playing) { return; }
-			SpriteWidget selectedWidget = timeline.SelectedWidget as SpriteWidget;
+			SpriteWidget selectedWidget = timeline.SelectedObject as SpriteWidget;
 
 			tsRemoveSprite.Enabled = tsAddEndFrame.Enabled = (selectedWidget != null);
 			tsCreateSequence.Enabled = true;
 			tsAddKeyframe.Enabled = false;
 			tsRemoveKeyframe.Enabled = false;
+			tsFrameType.Enabled = false;
 			if (selectedWidget != null)
 			{
 				tsRemoveSprite.Enabled = true;
@@ -308,6 +312,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				if (selectedWidget.SelectedFrame != null && selectedWidget.SelectedFrame.Time != 0)
 				{
 					tsRemoveKeyframe.Enabled = true;
+					tsFrameType.Enabled = true;
 				}
 			}
 		}
@@ -318,14 +323,14 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			if (openFileDialog1.ShowDialog(_character, "") == DialogResult.OK)
 			{
 				string src = openFileDialog1.FileName;
-				timeline.SelectData(timeline.CreateWidget(src)?.GetData());
+				timeline.SelectObject(timeline.CreateWidget(src));
 			}
 		}
 
 		private void AddKeyframe_Click(object sender, EventArgs e)
 		{
-			if (timeline.SelectedWidget == null) { return; }
-			SpriteWidget widget = timeline.SelectedWidget as SpriteWidget;
+			if (timeline.SelectedObject == null) { return; }
+			SpriteWidget widget = timeline.SelectedObject as SpriteWidget;
 			//TODO: Make this a command
 			LiveKeyframe frame = widget.Sprite.AddKeyframe(_time - widget.GetStart());
 			widget.SelectKeyframe(frame, null, false);
@@ -334,8 +339,8 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		private void RemoveSprite_Click(object sender, EventArgs e)
 		{
-			if (timeline.SelectedWidget == null) { return; }
-			if (MessageBox.Show($"Are you sure you want to remove {timeline.SelectedWidget.ToString()}?", "Remove Sprite", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
+			if (timeline.SelectedObject == null) { return; }
+			if (MessageBox.Show($"Are you sure you want to remove {timeline.SelectedObject.ToString()}?", "Remove Sprite", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
 			{
 				timeline.RemoveSelectedWidget();
 				UpdateToolbar();
@@ -344,8 +349,8 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		private void RemoveKeyframe_Click(object sender, EventArgs e)
 		{
-			if (timeline.SelectedWidget == null) { return; }
-			SpriteWidget widget = timeline.SelectedWidget as SpriteWidget;
+			if (timeline.SelectedObject == null) { return; }
+			SpriteWidget widget = timeline.SelectedObject as SpriteWidget;
 			LiveKeyframe frame = widget.SelectedFrame;
 			if (frame != null)
 			{
@@ -357,8 +362,8 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		private void CopyFirstFrame_Click(object sender, EventArgs e)
 		{
-			if (timeline.SelectedWidget == null) { return; }
-			SpriteWidget widget = timeline.SelectedWidget as SpriteWidget;
+			if (timeline.SelectedObject == null) { return; }
+			SpriteWidget widget = timeline.SelectedObject as SpriteWidget;
 			LiveSprite sprite = widget.Sprite;
 			if (sprite.Keyframes.Count > 0)
 			{
@@ -483,7 +488,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		private void tsCreateSequence_Click(object sender, EventArgs e)
 		{
-			SpriteWidget widget = timeline.SelectedWidget as SpriteWidget;
+			SpriteWidget widget = timeline.SelectedObject as SpriteWidget;
 			LiveSprite sprite = widget?.Sprite;
 			if (!CanBeSequenced(widget))
 			{
@@ -500,13 +505,21 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				if (form.Sprite == null)
 				{
 					sprite = timeline.CreateWidget(form.Frames[0])?.GetData() as LiveSprite;
+					sprite.Id = form.SequenceName;
 				}
-				AnimatedProperty prop = sprite.GetAnimationProperties("Src");
-				prop.Ease.SetValue(0, "linear");
-				sprite.Keyframes[0].Src = frames[0];
+				else
+				{
+					while (sprite.Keyframes.Count > 1)
+					{
+						sprite.Keyframes.RemoveAt(1);
+					}
+				}
+				sprite.GetBlockMetadata("Src", 0).Ease = "linear";
+				LiveSpriteKeyframe keyframe = sprite.Keyframes[0] as LiveSpriteKeyframe;
+				keyframe.Src = frames[0];
 				for (int i = 1; i < frames.Count; i++)
 				{
-					LiveKeyframe kf = sprite.AddKeyframe(i * form.Duration);
+					LiveSpriteKeyframe kf = sprite.AddKeyframe(i * form.Duration) as LiveSpriteKeyframe;
 					kf.Src = frames[i];
 				}
 			}
@@ -519,18 +532,89 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				return false;
 			}
 			LiveSprite sprite = widget.Sprite;
-			if (sprite.Keyframes.Count == 1)
+			if (sprite.AnimatedProperties.Count <= 3) //only "X", "Y", and "Src" are allowed
 			{
+				if (!sprite.AnimatedProperties.Contains("Src"))
+				{
+					return false;
+				}
+				if (sprite.AnimatedProperties.Count == 2 && !sprite.AnimatedProperties.Contains("X") && !sprite.AnimatedProperties.Contains("Y"))
+				{
+					return false;
+				}
+				if (sprite.AnimatedProperties.Count == 3 && (!sprite.AnimatedProperties.Contains("X") || !sprite.AnimatedProperties.Contains("Y")))
+				{
+					return false;
+				}
 				return true;
 			}
 
-			string path = sprite.Keyframes[0].Src;
-			if (!path.EndsWith("0"))
+			return false;
+		}
+
+		private void tsRefresh_Click(object sender, EventArgs e)
+		{
+			LiveImageCache.Refresh();
+			foreach (LiveSprite sprite in _pose.Sprites)
 			{
-				return false;
+				if (!string.IsNullOrEmpty(sprite.Src))
+				{
+					sprite.Image = LiveImageCache.Get(sprite.Src);
+					if (sprite.Image != null)
+					{
+						sprite.Width = sprite.Image.Width;
+						sprite.Height = sprite.Image.Height;
+					}
+					else
+					{
+						sprite.Width = 100;
+						sprite.Height = 100;
+					}
+				}
 			}
+			canvas.InvalidateCanvas();
+		}
 
-			return false;
+
+		private void Timeline_UIRequested(object sender, object data)
+		{
+			LiveSpriteKeyframe frame = data as LiveSpriteKeyframe;
+			if (frame != null)
+			{
+				openFileDialog1.UseAbsolutePaths = true;
+				if (openFileDialog1.ShowDialog(_character, frame.Src ?? "") == DialogResult.OK)
+				{
+					string src = openFileDialog1.FileName;
+					frame.Src = src;
+				}
+			}
+		}
+
+		private void tsExport_Click(object sender, EventArgs e)
+		{
+			if (_pose == null) { return; }
+			SavePose();
+			PoseExporter exporter = new PoseExporter();
+			exporter.SetPose(_sourcePose);
+			exporter.ShowDialog();
+		}
+
+		private void tsFrameType_Click(object sender, EventArgs e)
+		{
+			if (timeline.SelectedObject == null) { return; }
+			SpriteWidget widget = timeline.SelectedObject as SpriteWidget;
+			LiveKeyframe frame = widget.SelectedFrame;
+			if (frame != null)
+			{
+				HashSet<string> props = new HashSet<string>();
+				foreach (string p in widget.SelectedProperties)
+				{
+					props.Add(p);
+				}
+				ToggleKeyframeTypeCommand command = new ToggleKeyframeTypeCommand(widget.Sprite, frame, props);
+				_history.Commit(command);
+			}
+			UpdateToolbar();
 		}
 	}
 
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/ScenePreview.cs b/editor source/SPNATI Character Editor/EpilogueEditing/ScenePreview.cs
index be56e59a46e43797428e1a3216f7584ef7280b54..4ec0976fc07892db83f36f0803ff471fc14ecb23 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/ScenePreview.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/ScenePreview.cs	
@@ -86,10 +86,6 @@ namespace SPNATI_Character_Editor.EpilogueEditing
 			AddImage(scene.Background);
 			foreach (Directive directive in scene.Directives)
 			{
-				if (!string.IsNullOrEmpty(directive.Marker) && !Marker.CheckMarker(directive.Marker, Markers))
-				{
-					continue;
-				}
 				AddImage(directive.Src);
 				foreach (Keyframe frame in directive.Keyframes)
 				{
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/SimpleIK.cs b/editor source/SPNATI Character Editor/EpilogueEditing/SimpleIK.cs
deleted file mode 100644
index a4b120f23e892a1b295f5ec2c35fcc68474303c8..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/EpilogueEditing/SimpleIK.cs	
+++ /dev/null
@@ -1,44 +0,0 @@
-using System;
-using System.Drawing;
-
-namespace SPNATI_Character_Editor.EpilogueEditor
-{
-	public class SimpleIK
-	{
-		public void Solve(LiveSprite hand, PointF target)
-		{
-			if (hand.Parent == null) { return; }
-			LiveSprite Joint1 = hand.Parent;
-			if (Joint1.Parent == null) { return; }
-			LiveSprite Joint0 = Joint1.Parent;
-
-			PointF pos0 = Joint0.ToWorldPt(0, 0);
-			PointF pos1 = Joint1.ToWorldPt(0, 0);
-			PointF posHand = hand.ToWorldPt(0, 0);
-
-			float length0 = pos0.Distance(pos1);
-			float length1 = pos1.Distance(posHand);
-
-			//distance from origin to target
-			float length2 = pos0.Distance(target);
-
-			//inner angle alpha
-			float cosAngle0 = ((length2 * length2) + (length0 * length0) - (length1 * length1)) / (2 * length2 * length0);
-			float angle0 = (float)Math.Acos(cosAngle0) * MathUtil.Rad2Deg;
-
-			//inner angle beta
-			float cosAngle1 = ((length1 * length1) + (length0 * length0) - (length2 * length2)) / (2 * length1 * length0);
-			float angle1 = (float)Math.Acos(cosAngle1) * MathUtil.Rad2Deg;
-
-			//angle from origin and target
-			PointF diff = new PointF(target.X - pos0.X, target.Y - pos0.Y);
-			float atan = (float)Math.Atan2(diff.Y, diff.X) * MathUtil.Rad2Deg;
-
-			float jointAngle0 = atan - angle0;
-			float jointAngle1 = 180 - angle1;
-
-			Joint0.Rotation = jointAngle0;
-			Joint1.Rotation = jointAngle1;
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Timeline.Designer.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Timeline.Designer.cs
index a1fea76b69a5e20d71b5db6ed624e9c93bf4d5af..7d7598e353f19ae6bb9c1b0d96c5b6d8104223a9 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/Timeline.Designer.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Timeline.Designer.cs	
@@ -35,19 +35,6 @@
 			this.container = new Desktop.CommonControls.DBPanel();
 			this.panel = new Desktop.CommonControls.SelectablePanel();
 			this.tools = new System.Windows.Forms.ToolStrip();
-			this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
-			this.editMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
-			this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
-			this.tooltip = new System.Windows.Forms.ToolTip(this.components);
-			this.tmrTooltip = new System.Windows.Forms.Timer(this.components);
-			this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
-			this.tsUndo = new System.Windows.Forms.ToolStripMenuItem();
-			this.tsRedo = new System.Windows.Forms.ToolStripMenuItem();
-			this.tsCut = new System.Windows.Forms.ToolStripMenuItem();
-			this.tsCopy = new System.Windows.Forms.ToolStripMenuItem();
-			this.tsPaste = new System.Windows.Forms.ToolStripMenuItem();
-			this.tsDuplicate = new System.Windows.Forms.ToolStripMenuItem();
-			this.tsDelete = new System.Windows.Forms.ToolStripMenuItem();
 			this.tsFirst = new System.Windows.Forms.ToolStripButton();
 			this.tsPrevious = new System.Windows.Forms.ToolStripButton();
 			this.tsPlay = new System.Windows.Forms.ToolStripSplitButton();
@@ -56,8 +43,21 @@
 			this.playOnceWithRepeatsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
 			this.tsNext = new System.Windows.Forms.ToolStripButton();
 			this.tsLast = new System.Windows.Forms.ToolStripButton();
+			this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
 			this.tsZoomOut = new System.Windows.Forms.ToolStripButton();
 			this.tsZoomIn = new System.Windows.Forms.ToolStripButton();
+			this.editMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
+			this.tsUndo = new System.Windows.Forms.ToolStripMenuItem();
+			this.tsRedo = new System.Windows.Forms.ToolStripMenuItem();
+			this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
+			this.tsCut = new System.Windows.Forms.ToolStripMenuItem();
+			this.tsCopy = new System.Windows.Forms.ToolStripMenuItem();
+			this.tsPaste = new System.Windows.Forms.ToolStripMenuItem();
+			this.tsDuplicate = new System.Windows.Forms.ToolStripMenuItem();
+			this.tsDelete = new System.Windows.Forms.ToolStripMenuItem();
+			this.tooltip = new System.Windows.Forms.ToolTip(this.components);
+			this.tmrTooltip = new System.Windows.Forms.Timer(this.components);
+			this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
 			this.container.SuspendLayout();
 			this.tools.SuspendLayout();
 			this.editMenu.SuspendLayout();
@@ -75,13 +75,16 @@
 			this.panelHeader.Location = new System.Drawing.Point(0, 25);
 			this.panelHeader.Margin = new System.Windows.Forms.Padding(0);
 			this.panelHeader.Name = "panelHeader";
+			this.panelHeader.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.panelHeader.Size = new System.Drawing.Size(180, 110);
 			this.panelHeader.TabIndex = 4;
+			this.panelHeader.TabSide = Desktop.Skinning.TabSide.None;
 			this.panelHeader.TabStop = true;
 			this.panelHeader.Paint += new System.Windows.Forms.PaintEventHandler(this.panelHeader_Paint);
 			this.panelHeader.MouseDown += new System.Windows.Forms.MouseEventHandler(this.panelHeader_MouseDown);
 			this.panelHeader.MouseLeave += new System.EventHandler(this.panelHeader_MouseLeave);
 			this.panelHeader.MouseMove += new System.Windows.Forms.MouseEventHandler(this.panelHeader_MouseMove);
+			this.panelHeader.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.panel_PreviewKeyDown);
 			// 
 			// panelAxis
 			// 
@@ -90,12 +93,15 @@
 			this.panelAxis.Location = new System.Drawing.Point(180, 0);
 			this.panelAxis.Margin = new System.Windows.Forms.Padding(0);
 			this.panelAxis.Name = "panelAxis";
+			this.panelAxis.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.panelAxis.Size = new System.Drawing.Size(697, 25);
 			this.panelAxis.TabIndex = 3;
+			this.panelAxis.TabSide = Desktop.Skinning.TabSide.None;
 			this.panelAxis.Paint += new System.Windows.Forms.PaintEventHandler(this.panelAxis_Paint);
 			this.panelAxis.MouseDown += new System.Windows.Forms.MouseEventHandler(this.panelAxis_MouseDown);
 			this.panelAxis.MouseMove += new System.Windows.Forms.MouseEventHandler(this.panelAxis_MouseMove);
 			this.panelAxis.MouseUp += new System.Windows.Forms.MouseEventHandler(this.panelAxis_MouseUp);
+			this.panelAxis.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.panel_PreviewKeyDown);
 			// 
 			// container
 			// 
@@ -107,8 +113,10 @@
 			this.container.Location = new System.Drawing.Point(180, 25);
 			this.container.Margin = new System.Windows.Forms.Padding(0);
 			this.container.Name = "container";
+			this.container.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.container.Size = new System.Drawing.Size(697, 110);
 			this.container.TabIndex = 3;
+			this.container.TabSide = Desktop.Skinning.TabSide.None;
 			this.container.Scroll += new System.Windows.Forms.ScrollEventHandler(this.container_Scroll);
 			// 
 			// panel
@@ -117,8 +125,10 @@
 			this.panel.Location = new System.Drawing.Point(0, 0);
 			this.panel.Margin = new System.Windows.Forms.Padding(0);
 			this.panel.Name = "panel";
+			this.panel.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.panel.Size = new System.Drawing.Size(697, 110);
 			this.panel.TabIndex = 2;
+			this.panel.TabSide = Desktop.Skinning.TabSide.None;
 			this.panel.TabStop = true;
 			this.panel.Paint += new System.Windows.Forms.PaintEventHandler(this.panel_Paint);
 			this.panel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.panel_MouseDown);
@@ -147,115 +157,9 @@
 			this.tools.Padding = new System.Windows.Forms.Padding(0);
 			this.tools.Size = new System.Drawing.Size(180, 25);
 			this.tools.TabIndex = 6;
+			this.tools.Tag = "Surface";
 			this.tools.Text = "tools";
 			// 
-			// toolStripSeparator3
-			// 
-			this.toolStripSeparator3.Name = "toolStripSeparator3";
-			this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
-			// 
-			// editMenu
-			// 
-			this.editMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
-            this.tsUndo,
-            this.tsRedo,
-            this.toolStripSeparator2,
-            this.tsCut,
-            this.tsCopy,
-            this.tsPaste,
-            this.tsDuplicate,
-            this.tsDelete});
-			this.editMenu.Name = "editMenu";
-			this.editMenu.Size = new System.Drawing.Size(167, 164);
-			this.editMenu.Opening += new System.ComponentModel.CancelEventHandler(this.editMenu_Opening);
-			// 
-			// toolStripSeparator2
-			// 
-			this.toolStripSeparator2.Name = "toolStripSeparator2";
-			this.toolStripSeparator2.Size = new System.Drawing.Size(163, 6);
-			this.toolStripSeparator2.Visible = false;
-			// 
-			// tmrTooltip
-			// 
-			this.tmrTooltip.Tick += new System.EventHandler(this.tmrTooltip_Tick);
-			// 
-			// contextMenu
-			// 
-			this.contextMenu.Name = "contextMenu";
-			this.contextMenu.Size = new System.Drawing.Size(61, 4);
-			// 
-			// tsUndo
-			// 
-			this.tsUndo.Enabled = false;
-			this.tsUndo.Image = global::SPNATI_Character_Editor.Properties.Resources.Undo;
-			this.tsUndo.Name = "tsUndo";
-			this.tsUndo.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Z)));
-			this.tsUndo.Size = new System.Drawing.Size(166, 22);
-			this.tsUndo.Text = "Undo";
-			this.tsUndo.Visible = false;
-			this.tsUndo.Click += new System.EventHandler(this.tsUndo_Click);
-			// 
-			// tsRedo
-			// 
-			this.tsRedo.Enabled = false;
-			this.tsRedo.Image = global::SPNATI_Character_Editor.Properties.Resources.Redo;
-			this.tsRedo.Name = "tsRedo";
-			this.tsRedo.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Y)));
-			this.tsRedo.Size = new System.Drawing.Size(166, 22);
-			this.tsRedo.Text = "Redo";
-			this.tsRedo.Visible = false;
-			this.tsRedo.Click += new System.EventHandler(this.tsRedo_Click);
-			// 
-			// tsCut
-			// 
-			this.tsCut.Enabled = false;
-			this.tsCut.Image = global::SPNATI_Character_Editor.Properties.Resources.Cut;
-			this.tsCut.Name = "tsCut";
-			this.tsCut.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.X)));
-			this.tsCut.Size = new System.Drawing.Size(166, 22);
-			this.tsCut.Text = "Cut";
-			this.tsCut.Click += new System.EventHandler(this.tsCut_Click);
-			// 
-			// tsCopy
-			// 
-			this.tsCopy.Enabled = false;
-			this.tsCopy.Image = global::SPNATI_Character_Editor.Properties.Resources.Copy;
-			this.tsCopy.Name = "tsCopy";
-			this.tsCopy.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C)));
-			this.tsCopy.Size = new System.Drawing.Size(166, 22);
-			this.tsCopy.Text = "Copy";
-			this.tsCopy.Click += new System.EventHandler(this.tsCopy_Click);
-			// 
-			// tsPaste
-			// 
-			this.tsPaste.Enabled = false;
-			this.tsPaste.Image = global::SPNATI_Character_Editor.Properties.Resources.Paste;
-			this.tsPaste.Name = "tsPaste";
-			this.tsPaste.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.V)));
-			this.tsPaste.Size = new System.Drawing.Size(166, 22);
-			this.tsPaste.Text = "Paste";
-			this.tsPaste.Click += new System.EventHandler(this.tsPaste_Click);
-			// 
-			// tsDuplicate
-			// 
-			this.tsDuplicate.Enabled = false;
-			this.tsDuplicate.Image = global::SPNATI_Character_Editor.Properties.Resources.Duplicate;
-			this.tsDuplicate.Name = "tsDuplicate";
-			this.tsDuplicate.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D)));
-			this.tsDuplicate.Size = new System.Drawing.Size(166, 22);
-			this.tsDuplicate.Text = "Duplicate";
-			this.tsDuplicate.Click += new System.EventHandler(this.tsDuplicate_Click);
-			// 
-			// tsDelete
-			// 
-			this.tsDelete.Enabled = false;
-			this.tsDelete.Image = global::SPNATI_Character_Editor.Properties.Resources.Delete;
-			this.tsDelete.Name = "tsDelete";
-			this.tsDelete.ShortcutKeys = System.Windows.Forms.Keys.Delete;
-			this.tsDelete.Size = new System.Drawing.Size(166, 22);
-			this.tsDelete.Text = "Delete";
-			this.tsDelete.Click += new System.EventHandler(this.tsDelete_Click);
-			// 
 			// tsFirst
 			// 
 			this.tsFirst.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
@@ -334,6 +238,11 @@
 			this.tsLast.Text = "Jump to End";
 			this.tsLast.Click += new System.EventHandler(this.tsLast_Click);
 			// 
+			// toolStripSeparator3
+			// 
+			this.toolStripSeparator3.Name = "toolStripSeparator3";
+			this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
+			// 
 			// tsZoomOut
 			// 
 			this.tsZoomOut.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
@@ -350,10 +259,112 @@
 			this.tsZoomIn.Image = global::SPNATI_Character_Editor.Properties.Resources.ZoomIn;
 			this.tsZoomIn.ImageTransparentColor = System.Drawing.Color.Magenta;
 			this.tsZoomIn.Name = "tsZoomIn";
-			this.tsZoomIn.Size = new System.Drawing.Size(23, 20);
+			this.tsZoomIn.Size = new System.Drawing.Size(23, 22);
 			this.tsZoomIn.Text = "Zoom in";
 			this.tsZoomIn.Click += new System.EventHandler(this.tsZoomIn_Click);
 			// 
+			// editMenu
+			// 
+			this.editMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.tsUndo,
+            this.tsRedo,
+            this.toolStripSeparator2,
+            this.tsCut,
+            this.tsCopy,
+            this.tsPaste,
+            this.tsDuplicate,
+            this.tsDelete});
+			this.editMenu.Name = "editMenu";
+			this.editMenu.Size = new System.Drawing.Size(167, 164);
+			this.editMenu.Opening += new System.ComponentModel.CancelEventHandler(this.editMenu_Opening);
+			// 
+			// tsUndo
+			// 
+			this.tsUndo.Enabled = false;
+			this.tsUndo.Image = global::SPNATI_Character_Editor.Properties.Resources.Undo;
+			this.tsUndo.Name = "tsUndo";
+			this.tsUndo.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Z)));
+			this.tsUndo.Size = new System.Drawing.Size(166, 22);
+			this.tsUndo.Text = "Undo";
+			this.tsUndo.Visible = false;
+			this.tsUndo.Click += new System.EventHandler(this.tsUndo_Click);
+			// 
+			// tsRedo
+			// 
+			this.tsRedo.Enabled = false;
+			this.tsRedo.Image = global::SPNATI_Character_Editor.Properties.Resources.Redo;
+			this.tsRedo.Name = "tsRedo";
+			this.tsRedo.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Y)));
+			this.tsRedo.Size = new System.Drawing.Size(166, 22);
+			this.tsRedo.Text = "Redo";
+			this.tsRedo.Visible = false;
+			this.tsRedo.Click += new System.EventHandler(this.tsRedo_Click);
+			// 
+			// toolStripSeparator2
+			// 
+			this.toolStripSeparator2.Name = "toolStripSeparator2";
+			this.toolStripSeparator2.Size = new System.Drawing.Size(163, 6);
+			this.toolStripSeparator2.Visible = false;
+			// 
+			// tsCut
+			// 
+			this.tsCut.Enabled = false;
+			this.tsCut.Image = global::SPNATI_Character_Editor.Properties.Resources.Cut;
+			this.tsCut.Name = "tsCut";
+			this.tsCut.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.X)));
+			this.tsCut.Size = new System.Drawing.Size(166, 22);
+			this.tsCut.Text = "Cut";
+			this.tsCut.Click += new System.EventHandler(this.tsCut_Click);
+			// 
+			// tsCopy
+			// 
+			this.tsCopy.Enabled = false;
+			this.tsCopy.Image = global::SPNATI_Character_Editor.Properties.Resources.Copy;
+			this.tsCopy.Name = "tsCopy";
+			this.tsCopy.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C)));
+			this.tsCopy.Size = new System.Drawing.Size(166, 22);
+			this.tsCopy.Text = "Copy";
+			this.tsCopy.Click += new System.EventHandler(this.tsCopy_Click);
+			// 
+			// tsPaste
+			// 
+			this.tsPaste.Enabled = false;
+			this.tsPaste.Image = global::SPNATI_Character_Editor.Properties.Resources.Paste;
+			this.tsPaste.Name = "tsPaste";
+			this.tsPaste.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.V)));
+			this.tsPaste.Size = new System.Drawing.Size(166, 22);
+			this.tsPaste.Text = "Paste";
+			this.tsPaste.Click += new System.EventHandler(this.tsPaste_Click);
+			// 
+			// tsDuplicate
+			// 
+			this.tsDuplicate.Enabled = false;
+			this.tsDuplicate.Image = global::SPNATI_Character_Editor.Properties.Resources.Duplicate;
+			this.tsDuplicate.Name = "tsDuplicate";
+			this.tsDuplicate.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D)));
+			this.tsDuplicate.Size = new System.Drawing.Size(166, 22);
+			this.tsDuplicate.Text = "Duplicate";
+			this.tsDuplicate.Click += new System.EventHandler(this.tsDuplicate_Click);
+			// 
+			// tsDelete
+			// 
+			this.tsDelete.Enabled = false;
+			this.tsDelete.Image = global::SPNATI_Character_Editor.Properties.Resources.Delete;
+			this.tsDelete.Name = "tsDelete";
+			this.tsDelete.ShortcutKeys = System.Windows.Forms.Keys.Delete;
+			this.tsDelete.Size = new System.Drawing.Size(166, 22);
+			this.tsDelete.Text = "Delete";
+			this.tsDelete.Click += new System.EventHandler(this.tsDelete_Click);
+			// 
+			// tmrTooltip
+			// 
+			this.tmrTooltip.Tick += new System.EventHandler(this.tmrTooltip_Tick);
+			// 
+			// contextMenu
+			// 
+			this.contextMenu.Name = "contextMenu";
+			this.contextMenu.Size = new System.Drawing.Size(61, 4);
+			// 
 			// Timeline
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Timeline.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Timeline.cs
index 63fda9b741d7906ecc727928290a355bbb628012..6a14bb5f30e10e4907d837e6dc68774ee210e8c6 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/Timeline.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Timeline.cs	
@@ -7,10 +7,11 @@ using System.Linq;
 using System.Windows.Forms;
 using Desktop;
 using SPNATI_Character_Editor.Actions;
+using Desktop.Skinning;
 
 namespace SPNATI_Character_Editor.EpilogueEditor
 {
-	public partial class Timeline : UserControl
+	public partial class Timeline : UserControl, ISkinControl
 	{
 		private const int MajorTickHeight = 10;
 		private const int TickHeight = 6;
@@ -29,31 +30,32 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		public static Cursor HandOpen = LoadCursor(Properties.Resources.hand_open);
 		public static Cursor HandClosed = LoadCursor(Properties.Resources.hand_closed);
 		private NumberFormatInfo _timeFormatter;
-		private Brush _trackFill;
-		private Brush _trackFillAlternate;
-		private Brush _trackFillSelected;
-		private Brush _brushTimeline;
-		private Brush _brushWidgetHeader;
+		private SolidBrush _trackFill;
+		private SolidBrush _trackFillAlternate;
+		private SolidBrush _trackFillSelected;
+		private SolidBrush _timelineBack;
+		private SolidBrush _widgetHeaderFill;
 		private Pen _penTickMajor;
 		private Pen _penTick;
 		private Pen _penTickMinor;
-		private Pen _penCanvasTickMajor;
-		private Pen _penCanvasTickMinor;
+		private Pen _trackBorder;
+		private Pen _trackRowBorder;
 		private Font _fontTimeline;
-		private Brush _brushFont;
+		private SolidBrush _timelineFore;
+		private SolidBrush _widgetTitleFore;
 		private StringFormat _rowHeaderFormat;
-		private Brush _widgetSelectedFill;
-		private Pen _widgetOutline;
-		private Pen _widgetSelectedOutline;
+		private SolidBrush _widgetSelectedFill;
+		public static Pen WidgetOutline;
+		public static Pen WidgetSelectedOutline;
 		private Pen _timeLineAxis;
 		private Pen _timeLine;
 		private Pen _playbackLineAxis;
 		private Pen _playbackLine;
-		private Brush _iconHoverFill;
+		private SolidBrush _iconHoverFill;
 
 		private float _zoom = 1;
 		private ITimelineAction _pendingAction = null;
-		private ITimelineWidget _pendingWidget = null;
+		private ITimelineObject _pendingObject = null;
 		private ITimelineAction _currentAction = null;
 		private DateTime _lastTick;
 		private ITimelineWidget _headerHoverWidget = null;
@@ -62,17 +64,23 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		private int _headerX;
 		private int _headerY;
+		private DateTime _lastClick = DateTime.Now;
 
 		private List<ITimelineWidget> _widgets = new List<ITimelineWidget>();
-		private ITimelineWidget _selectedWidget;
+		private ITimelineObject _selectedObject;
+		private List<ITimelineBreak> _breaks = new List<ITimelineBreak>();
 
 		private PlaybackMode _playbackMode = PlaybackMode.Once;
+		public bool PlaybackAwaitingInput { get; set; }
+		public bool PauseOnBreaks { get; set; }
 
-		public event EventHandler<ITimelineWidget> WidgetSelected;
+		public event EventHandler<ITimelineObject> WidgetSelected;
 		public event EventHandler<float> TimeChanged;
 		public event EventHandler<float> PlaybackTimeChanged;
+		public event EventHandler<float> ElapsedTimeChanged;
 		public event EventHandler<DataSelectionArgs> DataSelected;
 		public event EventHandler<bool> PlaybackChanged;
+		public event EventHandler<object> UIRequested;
 
 		private UndoManager _history;
 		public UndoManager CommandHistory
@@ -88,9 +96,9 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			}
 		}
 
-		public ITimelineWidget SelectedWidget
+		public ITimelineObject SelectedObject
 		{
-			get { return _selectedWidget; }
+			get { return _selectedObject; }
 		}
 
 		private float _time = 0;
@@ -100,17 +108,6 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			set
 			{
 				UpdateMarker(value);
-				//int x = TimeToX(value);
-				//int left = container.HorizontalScroll.Value;
-				//int right = left + container.Width;
-				//if (x < left || x > right)
-				//{
-				//	//using (Control child = new Control() { Parent = container, Left = x })
-				//	//{
-				//	//	container.ScrollControlIntoView(child);
-				//	//}
-				//	container.HorizontalScroll.Value = Math.Max(container.HorizontalScroll.Minimum, Math.Max(container.HorizontalScroll.Maximum, x - (right - left) / 2));
-				//}
 			}
 		}
 
@@ -121,12 +118,30 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			set
 			{
 				if (_playbackTime == value) { return; }
+				ITimelineBreak brk = GetBreakBetween(_playbackTime, value);
+				if (brk != null && PauseOnBreaks)
+				{
+					value = brk.Time - 0.001f;
+					PlaybackAwaitingInput = true;
+				}
 				_playbackTime = value;
 				Redraw();
 				PlaybackTimeChanged?.Invoke(this, value);
 			}
 		}
 
+		private float _elapsedTime = 0;
+		public float ElapsedTime
+		{
+			get { return _elapsedTime; }
+			set
+			{
+				if (_elapsedTime == value) { return; }
+				_elapsedTime = value;
+				ElapsedTimeChanged?.Invoke(this, value);
+			}
+		}
+
 		public ITimelineData Data { get; private set; }
 
 		private static Cursor LoadCursor(byte[] array)
@@ -146,19 +161,20 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			_trackFill = new SolidBrush(Color.FromArgb(200, 200, 200));
 			_trackFillAlternate = new SolidBrush(Color.FromArgb(210, 210, 210));
 			_trackFillSelected = new SolidBrush(Color.FromArgb(230, 230, 255));
-			_brushTimeline = new SolidBrush(Color.FromArgb(230, 230, 230));
-			_brushWidgetHeader = new SolidBrush(Color.FromArgb(185, 185, 185));
+			_timelineBack = new SolidBrush(Color.FromArgb(230, 230, 230));
+			_widgetHeaderFill = new SolidBrush(Color.FromArgb(185, 185, 185));
 			_fontTimeline = new Font("Arial", 8);
-			_brushFont = new SolidBrush(Color.Black);
+			_timelineFore = new SolidBrush(Color.Black);
 			_penTickMajor = new Pen(Color.FromArgb(0, 0, 0));
 			_penTick = new Pen(Color.FromArgb(130, 130, 130));
 			_penTickMinor = new Pen(Color.FromArgb(170, 170, 170));
-			_penCanvasTickMinor = new Pen(Color.FromArgb(170, 170, 170));
-			_penCanvasTickMajor = new Pen(Color.FromArgb(150, 150, 150));
+			_trackRowBorder = new Pen(Color.FromArgb(170, 170, 170));
+			_trackBorder = new Pen(Color.FromArgb(150, 150, 150));
 			_rowHeaderFormat = new StringFormat() { LineAlignment = StringAlignment.Center, Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.LineLimit };
 			_widgetSelectedFill = new SolidBrush(Color.White);
-			_widgetOutline = new Pen(Color.Black);
-			_widgetSelectedOutline = new Pen(Color.White);
+			WidgetOutline = new Pen(Color.Black);
+			WidgetSelectedOutline = new Pen(Color.White);
+			_widgetTitleFore = new SolidBrush(Color.Black);
 			_timeLineAxis = new Pen(Color.Red, 3);
 			_timeLine = new Pen(Color.Red, 1);
 			_playbackLineAxis = new Pen(Color.DarkGreen, 3);
@@ -168,6 +184,63 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			container.MouseWheel += Panel_MouseWheel;
 
 			UpdateMarker(0);
+			OnUpdateSkin(SkinManager.Instance.CurrentSkin);
+		}
+
+		private void SetDefaultColors()
+		{
+			_trackFill.Color = Color.FromArgb(200, 200, 200);
+			_trackFillAlternate.Color = Color.FromArgb(210, 210, 210);
+			_trackFillSelected.Color = Color.FromArgb(230, 230, 255);
+			_timelineBack.Color = Color.FromArgb(230, 230, 230);
+			_widgetHeaderFill.Color = Color.FromArgb(185, 185, 185);
+			_timelineFore.Color = Color.Black;
+			_penTickMajor.Color = Color.FromArgb(0, 0, 0);
+			_penTick.Color = Color.FromArgb(130, 130, 130);
+			_penTickMinor.Color = Color.FromArgb(170, 170, 170);
+			_trackRowBorder.Color = Color.FromArgb(170, 170, 170);
+			_trackBorder.Color = Color.FromArgb(150, 150, 150);
+			_widgetSelectedFill.Color = Color.White;
+			WidgetOutline.Color = Color.Black;
+			WidgetSelectedOutline.Color = Color.White;
+			_widgetTitleFore.Color = Color.Black;
+			_timeLine.Color = _timeLineAxis.Color = Color.Red;
+			_playbackLine.Color = _playbackLineAxis.Color = Color.DarkGreen;
+			_iconHoverFill.Color = Color.FromArgb(50, 0, 0, 0);
+		}
+
+		public void OnUpdateSkin(Skin skin)
+		{
+			_timelineBack.Color = skin.Surface.Normal;
+			_timelineFore.Color = skin.Surface.ForeColor;
+			if (!skin.AppColors.ContainsKey("TimelineFore"))
+			{
+				SetDefaultColors();
+			}
+			else
+			{
+				_trackFill.Color = skin.GetAppColor("TimelineRow");
+				_trackFillAlternate.Color = skin.GetAppColor("TimelineRowAlt");
+				_trackFillSelected.Color = skin.GetAppColor("TimelineSelected");
+				_widgetTitleFore.Color = skin.GetAppColor("TimelineFore");
+				_widgetSelectedFill.Color = skin.GetAppColor("WidgetTitleSelected");
+				_widgetHeaderFill.Color = skin.GetAppColor("WidgetTitle");
+				WidgetSelectedOutline.Color = skin.Group == "Dark" ? Color.Black : Color.White;
+				WidgetOutline.Color = skin.GetAppColor("WidgetBorder");
+				_playbackLine.Color = _playbackLineAxis.Color = skin.GetAppColor("PlaybackBar");
+				_timeLine.Color = _timeLineAxis.Color = skin.GetAppColor("TimelineBar");
+				_iconHoverFill.Color = skin.PrimaryColor.Hover;
+
+				_penTickMajor.Color = ColorSet.BlendColor(_timelineBack.Color, _timelineFore.Color, 0.5f);
+				_penTickMinor.Color = ColorSet.BlendColor(_timelineBack.Color, _timelineFore.Color, 0.3f);
+				_trackBorder.Color = skin.GetAppColor("TimelineRowBorder");
+				_trackRowBorder.Color = skin.GetAppColor("TimelineSubRowBorder");
+			}
+
+			foreach (ITimelineWidget widget in _widgets)
+			{
+				widget.UpdateSkin(skin);
+			}
 		}
 
 		private void Timeline_Load(object sender, EventArgs e)
@@ -188,11 +261,11 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		{
 			tsUndo.Enabled = CommandHistory.CanUndo();
 			tsRedo.Enabled = CommandHistory.CanRedo();
-			if (_selectedWidget != null)
+			if (_selectedObject != null)
 			{
-				if (!_widgets.Contains(_selectedWidget))
+				if (!_widgets.Contains(_selectedObject))
 				{
-					SelectWidget(null);
+					SelectObject(null);
 				}
 				else
 				{
@@ -205,7 +278,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		private void UpdateEditMenu()
 		{
 			WidgetSelectionArgs args = new WidgetSelectionArgs(this, SelectionType.Select, ModifierKeys);
-			_selectedWidget?.UpdateSelection(args);
+			_selectedObject?.UpdateSelection(args);
 			UpdateButtons(args);
 		}
 
@@ -219,11 +292,15 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			Data.WidgetRemoved += Data_WidgetRemoved;
 			foreach (ITimelineWidget widget in data.CreateWidgets(this))
 			{
-				AddWidget(widget, _widgets.Count);
+				AddObject(widget, _widgets.Count);
+			}
+			foreach (ITimelineBreak timeBreak in data.CreateBreaks(this))
+			{
+				AddBreak(timeBreak);
 			}
 			ResizeTimeline();
 			UpdateMarker(0);
-			SelectWidget(null);
+			SelectObject(null);
 			AutoZoom();
 		}
 
@@ -238,21 +315,22 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				Data = null;
 			}
 			_widgets.Clear();
+			_breaks.Clear();
 		}
 
 		private void Data_WidgetMoved(object sender, WidgetCreationArgs args)
 		{
-			MoveWidget(args.Widget, args.Index);
+			MoveObject(args.Widget, args.Index);
 		}
 
 		private void Data_WidgetRemoved(object sender, WidgetCreationArgs args)
 		{
-			RemoveWidget(args.Widget);
+			RemoveObject(args.Widget);
 		}
 
 		private void Data_WidgetCreated(object sender, WidgetCreationArgs args)
 		{
-			AddWidget(args.Widget, args.Index);
+			AddObject(args.Widget, args.Index);
 		}
 
 		public ITimelineWidget CreateWidget(object context)
@@ -260,7 +338,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			//TODO: This needs to be a command to be undoable
 			if (Data == null) { return null; }
 			ITimelineWidget widget = Data.CreateWidget(this, _time, context);
-			AddWidget(widget, _widgets.Count);
+			AddObject(widget, _widgets.Count);
 			return widget;
 		}
 
@@ -269,51 +347,92 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			//TODO: This needs to be a command to be undoable
 			if (Data == null) { return; }
 			ITimelineWidget widget = Data.CreateWidget(this, _time, data, index);
-			AddWidget(widget, index);
+			AddObject(widget, index);
+			SelectObject(widget);
+			SelectData(data);
 		}
 
 		public void RemoveSelectedWidget()
 		{
-			if (_selectedWidget == null) { return; }
-			DeleteWidgetCommand action = new DeleteWidgetCommand(Data, _selectedWidget);
+			if (_selectedObject == null) { return; }
+			DeleteWidgetCommand action = new DeleteWidgetCommand(Data, _selectedObject);
 			_history?.Commit(action);
 		}
 
-		private void AddWidget(ITimelineWidget widget, int index)
+		public void AddBreak(ITimelineBreak timeBreak)
 		{
-			if (index == -1)
+			AddObject(timeBreak, -1);
+		}
+
+		private void AddObject(ITimelineObject obj, int index)
+		{
+			ITimelineWidget widget = obj as ITimelineWidget;
+			if (widget != null)
 			{
-				_widgets.Add(widget);
+				if (index == -1)
+				{
+					_widgets.Add(widget);
+				}
+				else
+				{
+					_widgets.Insert(index, widget);
+				}
+				widget.Invalidated += Widget_Invalidated;
+				ResizeTimeline();
 			}
-			else
+			else if (obj is ITimelineBreak)
 			{
-				_widgets.Insert(index, widget);
+				ITimelineBreak brk = obj as ITimelineBreak;
+				_breaks.Add(brk);
 			}
-			widget.Invalidated += Widget_Invalidated;
-			ResizeTimeline();
 			Redraw();
 		}
 
-		private void RemoveWidget(ITimelineWidget widget)
+		private void RemoveObject(ITimelineObject obj)
 		{
-			_widgets.Remove(widget);
-			widget.Invalidated -= Widget_Invalidated;
-			ResizeTimeline();
+			if (obj is ITimelineWidget)
+			{
+				ITimelineWidget widget = obj as ITimelineWidget;
+				_widgets.Remove(widget);
+				widget.Invalidated -= Widget_Invalidated;
+				ResizeTimeline();
+			}
+			else if (obj is ITimelineBreak)
+			{
+				_breaks.Remove(obj as ITimelineBreak);
+			}
 			Redraw();
 		}
 
-		private void MoveWidget(ITimelineWidget widget, int index)
+		private void MoveObject(ITimelineObject obj, int index)
 		{
-			int track = _widgets.IndexOf(widget);
-
-			_widgets.RemoveAt(track);
-			if (index >= _widgets.Count || index == -1)
+			if (obj is ITimelineWidget)
 			{
-				_widgets.Add(widget);
+				ITimelineWidget widget = obj as ITimelineWidget;
+				int track = _widgets.IndexOf(widget);
+
+				_widgets.RemoveAt(track);
+				if (index >= _widgets.Count || index == -1)
+				{
+					_widgets.Add(widget);
+				}
+				else
+				{
+					_widgets.Insert(index, widget);
+				}
 			}
-			else
+			else if (obj is ITimelineBreak)
 			{
-				_widgets.Insert(index, widget);
+				ITimelineBreak brk = obj as ITimelineBreak;
+				_breaks.RemoveAt(index);
+				if (index >= _breaks.Count || index == -1)
+				{
+					_breaks.Add(brk);
+				}
+				else
+				{
+					_breaks.Insert(index, brk);
+				}
 			}
 		}
 
@@ -324,20 +443,28 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			TimeSpan elapsed = now - _lastTick;
 			float elapsedSec = (float)elapsed.TotalSeconds;
 			_lastTick = now;
-			float time = _playbackTime + elapsedSec;
-			if (time > duration)
+
+			if (!PlaybackAwaitingInput)
 			{
-				switch (_playbackMode)
+				float time = _playbackTime + elapsedSec;
+				if (time > duration)
 				{
-					case PlaybackMode.Once:
-						EnablePlayback(false);
-						break;
-					case PlaybackMode.Looping:
-						time -= duration;
-						break;
+					switch (_playbackMode)
+					{
+						case PlaybackMode.Once:
+							EnablePlayback(false);
+							break;
+						case PlaybackMode.Looping:
+							time -= duration;
+							break;
+					}
 				}
+				PlaybackTime = time;
 			}
-			PlaybackTime = time;// (_playbackTime + elapsedSec) % duration;
+
+			float elapsedTime = _elapsedTime + elapsedSec;
+			ElapsedTime = elapsedTime;
+
 			Redraw();
 		}
 
@@ -487,7 +614,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			int height = panelAxis.Height;
 			int startX = container.HorizontalScroll.Value;
 			Graphics g = e.Graphics;
-			g.FillRectangle(_brushTimeline, 0, 0, panelAxis.Width, height);
+			g.FillRectangle(_timelineBack, 0, 0, panelAxis.Width, height);
 			g.DrawLine(_penTickMajor, 0, height, panelAxis.Width, height);
 			g.DrawLine(_penTickMajor, 0, panelAxis.Height - 1, panelAxis.Width, panelAxis.Height - 1);
 			int step = 0;
@@ -510,7 +637,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				if (major || (_zoom > 1.25f && step % 5 == 0))
 				{
 					major = true;
-					g.DrawString(time.ToString("0.00", _timeFormatter), _fontTimeline, _brushFont, x + 1, 0);
+					g.DrawString(time.ToString("0.00", _timeFormatter), _fontTimeline, _timelineFore, x + 1, 0);
 				}
 
 				if (major)
@@ -559,7 +686,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				{
 					int rowCount = widget.GetRowCount();
 					int trackY = y;
-					if (widget == _selectedWidget)
+					if (widget == _selectedObject)
 					{
 						g.FillRectangle(_trackFillSelected, 0, trackY + 1, panelHeader.Width, rowCount * RowHeight - 1);
 					}
@@ -567,12 +694,12 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 					for (int row = 0; row < rowCount; row++)
 					{
 						int rowY = trackY + row * RowHeight;
-						if (row == 0 || _selectedWidget == widget)
+						if (row == 0 || _selectedObject == widget)
 						{
 							bool highlighted = widget.IsRowHighlighted(row);
 							if (row == 0 || highlighted)
 							{
-								g.FillRectangle(_selectedWidget == widget && highlighted ? _widgetSelectedFill : _brushWidgetHeader, 0, rowY + 1, width, RowHeight - 1);
+								g.FillRectangle(_selectedObject == widget && highlighted ? _widgetSelectedFill : _widgetHeaderFill, 0, rowY + 1, width, RowHeight - 1);
 							}
 						}
 						string label = widget.GetLabel(row);
@@ -587,10 +714,11 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 							g.DrawImage(thumbnail, new Rectangle(left + 2, rowY + 1, RowHeight - 2, RowHeight - 2), new Rectangle(0, 0, thumbnail.Width, thumbnail.Height), GraphicsUnit.Pixel);
 							left += RowHeight;
 						}
-						g.DrawString(label, _fontTimeline, _brushFont, new Rectangle(left, rowY, width - left, RowHeight), _rowHeaderFormat);
-
 						int headerIconCount = widget.GetHeaderIconCount(row);
 						int iconLeft = panelHeader.Width - IconPadding - IconSize;
+
+						g.DrawString(label, _fontTimeline, _widgetTitleFore, new Rectangle(left, rowY, width - left - (panelHeader.Width - iconLeft), RowHeight), _rowHeaderFormat);
+
 						int iconTop = rowY + RowHeight / 2 - IconSize / 2;
 						for (int i = 0; i < headerIconCount; i++)
 						{
@@ -604,7 +732,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 							iconLeft -= IconSize + IconPadding;
 						}
 
-						g.DrawLine(_penCanvasTickMinor, 0, rowY + RowHeight, width, rowY + RowHeight);
+						g.DrawLine(_trackRowBorder, 0, rowY + RowHeight, width, rowY + RowHeight);
 
 						if (y > panelHeader.Height)
 						{
@@ -617,9 +745,9 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				{
 					y += RowHeight;
 				}
-				g.DrawLine(_penCanvasTickMajor, 0, y, panelHeader.Width, y);
+				g.DrawLine(_trackBorder, 0, y, panelHeader.Width, y);
 			}
-			g.DrawLine(_penCanvasTickMajor, panelHeader.Width - 1, 0, panelHeader.Width - 1, panelHeader.Height);
+			g.DrawLine(_trackBorder, panelHeader.Width - 1, 0, panelHeader.Width - 1, panelHeader.Height);
 		}
 
 		private void panel_Paint(object sender, PaintEventArgs e)
@@ -630,6 +758,8 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			float duration = Duration;
 
 			float pps = PixelsPerSecond * _zoom;
+
+			//widget backgrounds
 			int y = 0;
 			for (int i = 0; i < _widgets.Count; i++)
 			{
@@ -639,10 +769,10 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				if (widget != null)
 				{
 					rowCount = widget.GetRowCount();
-					trackHeight = widget.GetRowCount() * RowHeight;
+					trackHeight = rowCount * RowHeight;
 				}
 				int trackY = y;
-				if (_selectedWidget == widget)
+				if (_selectedObject == widget)
 				{
 					g.FillRectangle(_trackFillSelected, 0, y + 1, panel.Width, trackHeight - 1);
 				}
@@ -655,10 +785,41 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				{
 					//grid line
 					int rowY = y + r * RowHeight + RowHeight;
-					g.DrawLine(r < rowCount - 1 ? _penCanvasTickMinor : _penCanvasTickMajor, 0, rowY, panel.Width, rowY);
+					g.DrawLine(r < rowCount - 1 ? _trackRowBorder : _trackBorder, 0, rowY, panel.Width, rowY);
 				}
 				DrawTickMarks(g, y, trackHeight);
 
+				if (widget != null)
+				{
+					y += rowCount * RowHeight;
+				}
+				else
+				{
+					y += RowHeight;
+				}
+			}
+
+			//break backgrounds
+			for (int i = 0; i < _breaks.Count; i++)
+			{
+				ITimelineBreak brk = _breaks[i];
+				brk.DrawBackground(g, pps, panel.Height, brk == _selectedObject);
+			}
+
+			//widgets
+			y = 0;
+			for (int i = 0; i < _widgets.Count; i++)
+			{
+				ITimelineWidget widget = _widgets[i];
+				int trackHeight = RowHeight;
+				int rowCount = 1;
+				if (widget != null)
+				{
+					rowCount = widget.GetRowCount();
+					trackHeight = rowCount * RowHeight;
+				}
+				int trackY = y;
+
 				if (widget != null)
 				{
 					int count = rowCount;
@@ -666,28 +827,13 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 					float length = widget.GetLength(duration);
 					int x = TimeToX(start);
 
-					//background
-					int top = trackY;
-					int widgetWidth = TimeToX(length);
-					g.FillRectangle(widget.GetFillBrush(), x, top, widgetWidth, count * RowHeight);
-
-					//outline
-					int width = TimeToX(length);
-					g.DrawRectangle(_widgetOutline, x, top, width, count * RowHeight - 1);
-					if (_selectedWidget == widget)
-					{
-						g.DrawRectangle(_widgetSelectedOutline, x + 2, top + 2, width - 4, count * RowHeight - 5);
-					}
-
 					//contents
 					for (int row = 0; row < count; row++)
 					{
 						int rowY = trackY + row * RowHeight;
-						//contents
-						widget.DrawContents(g, row, x + 1, rowY + 1, pps, widgetWidth - 1, RowHeight - 2);
+						widget.DrawContents(g, row, x + 1, rowY + 1, pps, RowHeight - 2);
 					}
 
-
 					y += rowCount * RowHeight;
 				}
 				else
@@ -696,6 +842,13 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				}
 			}
 
+			//breaks
+			for (int i = 0; i < _breaks.Count; i++)
+			{
+				ITimelineBreak brk = _breaks[i];
+				brk.Draw(g, pps, panel.Height, brk == _selectedObject);
+			}
+
 			int timeX;
 			if (tmrTick.Enabled)
 			{
@@ -724,10 +877,10 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				}
 				bool major = step % MajorTickFrequency == 0;
 				int x = TimeToX(time);
-				Pen canvasPen = _penCanvasTickMinor;
+				Pen canvasPen = _trackRowBorder;
 				if (major)
 				{
-					canvasPen = _penCanvasTickMajor;
+					canvasPen = _trackBorder;
 				}
 				g.DrawLine(canvasPen, x, y, x, y + height);
 				step++;
@@ -757,47 +910,68 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		private void panelHeader_MouseDown(object sender, MouseEventArgs e)
 		{
-			int startY = container.VerticalScroll.Value;
-			int row = (e.Y + startY) / RowHeight; //absolute row
-			int currentRow = 0;
-			foreach (ITimelineWidget widget in _widgets)
+			if (e.Button == MouseButtons.Left)
 			{
-				int count = widget == null ? 1 : widget.GetRowCount();
-				if (currentRow + count > row)
+				int startY = container.VerticalScroll.Value;
+				int row = (e.Y + startY) / RowHeight; //absolute row
+				int currentRow = 0;
+				foreach (ITimelineWidget widget in _widgets)
 				{
-					if (currentRow == row)
+					int count = widget == null ? 1 : widget.GetRowCount();
+					if (currentRow + count > row)
 					{
-						//clicking collapsible button on title row
-						if (widget != null && e.X <= Properties.Resources.Expand.Width && widget.IsCollapsible)
+						if (currentRow == row)
 						{
-							widget.IsCollapsed = !widget.IsCollapsed;
-							panel.Invalidate();
-							ResizeTimeline();
-							return;
+							//clicking collapsible button on title row
+							if (widget != null && e.X <= Properties.Resources.Expand.Width && widget.IsCollapsible)
+							{
+								widget.IsCollapsed = !widget.IsCollapsed;
+								panel.Invalidate();
+								ResizeTimeline();
+								return;
+							}
 						}
-					}
-					//clicking any row within the widget
-					if (widget != null)
-					{
-						SelectWidget(widget);
-
-						int track;
-						int r = YToRow(e.Y + startY, out track);
-						int icon = GetHeaderIconIndex(e.X, widget, r);
-						WidgetActionArgs args = GetWidgetArgs(_time, e.Y + startY);
-						if (icon >= 0)
+						//clicking any row within the widget
+						if (widget != null)
 						{
-							widget.OnClickHeaderIcon(args, icon);
+							bool doubleClick = false;
+							DateTime clickTime = DateTime.Now;
+							if (_selectedObject == widget && (clickTime - _lastClick).TotalMilliseconds < 200)
+							{
+								doubleClick = true;
+							}
+							_lastClick = clickTime;
+
+							SelectObject(widget);
+
+							int track;
+							int r = YToRow(e.Y + startY, out track);
+							int icon = GetHeaderIconIndex(e.X, widget, r);
+							WidgetActionArgs args = GetWidgetArgs(_time, e.Y + startY);
+							if (icon >= 0)
+							{
+								widget.OnClickHeaderIcon(args, icon);
+							}
+							else
+							{
+								if (doubleClick)
+								{
+									widget.OnDoubleClickHeader(args);
+								}
+								else
+								{
+									widget.OnClickHeader(args);
+								}
+							}
+							WidgetSelectionArgs selectionArgs = new WidgetSelectionArgs(this, SelectionType.Reselect, ModifierKeys);
+							widget.UpdateSelection(selectionArgs);
+							UpdateButtons(selectionArgs);
 						}
-						widget.OnClickHeader(args);
-						WidgetSelectionArgs selectionArgs = new WidgetSelectionArgs(this, SelectionType.Reselect, ModifierKeys);
-						widget.UpdateSelection(selectionArgs);
-						UpdateButtons(selectionArgs);
+						panelHeader.Invalidate();
+						return;
 					}
-					panelHeader.Invalidate();
-					return;
+					currentRow += count;
 				}
-				currentRow += count;
 			}
 		}
 
@@ -842,21 +1016,41 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			if (e.Button == MouseButtons.None)
 			{
 				ITimelineAction old = _pendingAction;
+				ITimelineBreak brk = GetBreakUnderCursor(e.X);
 				ITimelineWidget widget = GetWidgetUnderCursor(e.Y);
-				if (widget != _pendingWidget && _pendingWidget != null)
+				ITimelineObject hoverObj = null;
+				ITimelineAction hoverAction = null;
+				ITimelineAction brkAction = GetAction(brk, e.X, e.Y);
+				ITimelineAction widgetAction = GetAction(widget, e.X, e.Y);
+				if (_selectedObject != brk && widgetAction != null && !(widgetAction is SelectAction))
 				{
-					_pendingWidget.OnMouseOut();
+					hoverObj = widget;
+					hoverAction = widgetAction;
 				}
-				if (widget != null)
+				else
+				{
+					hoverObj = brk;
+					hoverAction = brkAction;
+				}
+				if (hoverObj != _pendingObject && _pendingObject != null)
+				{
+					_pendingObject.OnMouseOut();
+				}
+				if (hoverObj != null)
 				{
-					_pendingAction = GetAction(widget, e.X, e.Y);
+					_pendingAction = hoverAction;
 					if (_pendingAction != null || old != null)
 					{
-						_pendingWidget = widget;
+						_pendingObject = hoverObj;
 						panel.Invalidate();
 					}
 					Cursor = (_pendingAction != null ? _pendingAction.GetHoverCursor() : Cursors.Default);
 				}
+				else
+				{
+					_pendingAction = null;
+					Cursor = Cursors.Default;
+				}
 			}
 			else if (_currentAction != null)
 			{
@@ -878,18 +1072,38 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			{
 				int track;
 				YToRow(e.Y, out track);
-				ITimelineWidget widget = GetWidgetUnderCursor(e.Y);
+				ITimelineObject widget = GetBreakUnderCursor(e.X);
+				ITimelineWidget overWidget = GetWidgetUnderCursor(e.Y);
+				if (widget == null || _pendingObject is ITimelineWidget)
+				{
+					widget = overWidget;
+				}
 
-				if (_selectedWidget != widget)
+				bool doubleClick = false;
+				DateTime clickTime = DateTime.Now;
+				if (_selectedObject == widget && (clickTime - _lastClick).TotalMilliseconds < 200)
 				{
-					SelectWidget(widget);
+					doubleClick = true;
 				}
+				_lastClick = clickTime;
 
-				if (widget != null && _pendingAction != null)
+				if (_selectedObject != widget)
 				{
-					_currentAction = _pendingAction;
-					_pendingAction = null;
-					StartAction(XToTime(e.X), e.Y);
+					SelectObject(widget);
+				}
+
+				if (widget != null)
+				{
+					if (doubleClick)
+					{
+						widget.OnDoubleClick(GetWidgetArgs(CurrentTime, e.Y));
+					}
+					else if (_pendingAction != null)
+					{
+						_currentAction = _pendingAction;
+						_pendingAction = null;
+						StartAction(XToTime(e.X), e.Y);
+					}
 				}
 			}
 		}
@@ -899,37 +1113,37 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			EndAction();
 		}
 
-		private void SelectWidget(ITimelineWidget widget)
+		public void SelectObject(ITimelineObject widget)
 		{
-			ApplyWidgetSelection(widget);
+			ApplyObjectSelection(widget);
 		}
 
-		public void ApplyWidgetSelection(ITimelineWidget widget)
+		public void ApplyObjectSelection(ITimelineObject widget)
 		{
 			WidgetSelectionArgs args = new WidgetSelectionArgs(this, SelectionType.Select, ModifierKeys);
-			if (widget != _selectedWidget)
+			if (widget != _selectedObject)
 			{
-				if (_selectedWidget != null)
+				if (_selectedObject != null)
 				{
 					args.IsSelected = SelectionType.Deselect;
-					_selectedWidget.OnWidgetSelectionChanged(args);
+					_selectedObject.OnWidgetSelectionChanged(args);
 				}
-				_selectedWidget = widget;
+				_selectedObject = widget;
 				args.IsSelected = SelectionType.Select;
 			}
 			else
 			{
 				args.IsSelected = SelectionType.Reselect;
 			}
-			if (_selectedWidget != null)
+			if (_selectedObject != null)
 			{
-				_selectedWidget.OnWidgetSelectionChanged(args);
+				_selectedObject.OnWidgetSelectionChanged(args);
 				tsNext.Enabled = true;
 				tsPrevious.Enabled = true;
 			}
 			else
 			{
-				Data.UpdateSelection(args);
+				Data?.UpdateSelection(args);
 				tsNext.Enabled = false;
 				tsPrevious.Enabled = false;
 			}
@@ -937,7 +1151,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 			if (args.IsSelected == SelectionType.Select)
 			{
-				WidgetSelected?.Invoke(this, _selectedWidget);
+				WidgetSelected?.Invoke(this, _selectedObject);
 			}
 
 			Redraw();
@@ -961,7 +1175,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			ITimelineWidget widget = _widgets.Find(w => w.GetData() == data);
 			if (widget != null)
 			{
-				SelectWidget(widget);
+				SelectObject(widget);
 			}
 		}
 
@@ -974,6 +1188,20 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			DataSelected?.Invoke(this, new DataSelectionArgs(data, previewData));
 		}
 
+		private ITimelineBreak GetBreakUnderCursor(int x)
+		{
+			for (int i = _breaks.Count - 1; i >= 0; i--)
+			{
+				ITimelineBreak brk = _breaks[i];
+				int brkX = TimeToX(brk.Time);
+				if (Math.Abs(brkX - x) <= 5)
+				{
+					return brk;
+				}
+			}
+			return null;
+		}
+
 		private ITimelineWidget GetWidgetUnderCursor(int y)
 		{
 			int track;
@@ -1006,7 +1234,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				History = _history,
 				Timeline = this,
 				Data = Data,
-				Widget = _selectedWidget,
+				Widget = _selectedObject,
 				Time = time,
 				Row = row,
 				SnapIncrement = _tickResolution,
@@ -1045,10 +1273,10 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 				_currentAction.Finish();
 				_currentAction = null;
 				Cursor = Cursors.Default;
-				if (_selectedWidget != null)
+				if (_selectedObject != null)
 				{
 					WidgetSelectionArgs args = new WidgetSelectionArgs(this, SelectionType.Select, ModifierKeys);
-					_selectedWidget.UpdateSelection(args);
+					_selectedObject.UpdateSelection(args);
 					UpdateButtons(args);
 				}
 			}
@@ -1059,6 +1287,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			if (e.Button == MouseButtons.Left)
 			{
 				//placing the time marker
+				panel.Select();
 				_currentAction = new TimelineDragAction(this);
 				float time = XToTime(e.X + container.HorizontalScroll.Value);
 				float inverse = 1 / _tickResolution * 2;
@@ -1085,6 +1314,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 						time = (float)Math.Round(Math.Round(time * inverse) / inverse, 2);
 					}
 					UpdateAction(time, 0);
+					panelAxis.Update();
 				}
 			}
 		}
@@ -1101,9 +1331,9 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		public void FinalizeTimeMovement()
 		{
-			if (_selectedWidget != null)
+			if (_selectedObject != null)
 			{
-				_selectedWidget.OnTimeChanged(GetOperationArgs());
+				_selectedObject.OnTimeChanged(GetOperationArgs());
 			}
 		}
 
@@ -1113,18 +1343,17 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		/// <param name="x"></param>
 		/// <param name="y"></param>
 		/// <returns></returns>
-		private ITimelineAction GetAction(ITimelineWidget widget, int x, int y)
+		private ITimelineAction GetAction(ITimelineObject widget, int x, int y)
 		{
 			float pps = PixelsPerSecond * _zoom;
 			if (widget != null)
 			{
-				_pendingWidget = widget;
 				float start = widget.GetStart();
 				int track;
 				int row = YToRow(y, out track);
-				return widget.GetAction(x - TimeToX(start), TimeToX(widget.GetLength(Duration)), XToTime(x), row, TimeToX(Duration), pps);
+				return widget.GetAction(x - TimeToX(start), XToTime(x), row, TimeToX(Duration), pps);
 			}
-			return _selectedWidget != null ? null : new SelectAction();
+			return _selectedObject != null ? null : new SelectAction();
 		}
 
 		/// <summary>
@@ -1141,10 +1370,18 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		private void tsPlay_Click(object sender, EventArgs e)
 		{
+			if (ModifierKeys.HasFlag(Keys.Control)) { return; }
 			EnablePlayback(!tmrTick.Enabled);
 		}
 
-		private void EnablePlayback(bool enabled)
+		public void ResumePlayback()
+		{
+			if (!PlaybackAwaitingInput) { return; }
+			PlaybackAwaitingInput = false;
+			_playbackTime += 0.001f;
+		}
+
+		public void EnablePlayback(bool enabled)
 		{
 			if (tmrTick.Enabled == enabled)
 			{
@@ -1152,7 +1389,10 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			}
 			if (enabled)
 			{
-				PlaybackTime = 0;
+				PlaybackAwaitingInput = false;
+				_playbackTime = CurrentTime - 0.01f;
+				PlaybackTime = (CurrentTime < Duration ? CurrentTime : 0);
+				ElapsedTime = 0;
 			}
 			tmrTick.Enabled = enabled;
 			_lastTick = DateTime.Now;
@@ -1176,7 +1416,6 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 						tsPlay.Image = Properties.Resources.TimelinePlayLoops;
 						break;
 				}
-				
 			}
 			PlaybackChanged?.Invoke(this, tmrTick.Enabled);
 			foreach (ITimelineWidget widget in _widgets)
@@ -1190,6 +1429,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		{
 			UpdateMarker(0);
 			PlaybackTime = 0;
+			ElapsedTime = 0;
 		}
 
 		private void tsLast_Click(object sender, EventArgs e)
@@ -1215,11 +1455,11 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			if (tmrTick.Enabled) { return; }
 			if (this.ContainsActiveControl())
 			{
-				if (_selectedWidget == null) { return; }
+				if (_selectedObject == null) { return; }
 				WidgetOperationArgs args = GetOperationArgs();
 				args.IsSilent = true;
-				_selectedWidget.OnCopy(args);
-				_selectedWidget.OnDelete(args);
+				_selectedObject.OnCopy(args);
+				_selectedObject.OnDelete(args);
 				tsPaste.Enabled = true;
 			}
 			else
@@ -1233,9 +1473,9 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 		{
 			if (this.ContainsActiveControl())
 			{
-				if (_selectedWidget == null) { return; }
+				if (_selectedObject == null) { return; }
 				WidgetOperationArgs args = GetOperationArgs();
-				_selectedWidget.OnCopy(args);
+				_selectedObject.OnCopy(args);
 
 				tsPaste.Enabled = true;
 			}
@@ -1250,7 +1490,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			if (this.ContainsActiveControl())
 			{
 				WidgetOperationArgs args = GetOperationArgs();
-				if (_selectedWidget != null && _selectedWidget.OnPaste(args))
+				if (_selectedObject != null && _selectedObject.OnPaste(args))
 				{
 					return;
 				}
@@ -1267,21 +1507,17 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			if (this.ContainsActiveControl())
 			{
 				WidgetOperationArgs args = GetOperationArgs();
-				_selectedWidget?.OnDuplicate(args);
+				_selectedObject?.OnDuplicate(args);
 			}
-
-			//int track = _selectedWidget != null ? _widgets.IndexOf(_selectedWidget) : -1;
-			//DuplicateWidgetAction action = new DuplicateWidgetAction(this, Data, _selectedWidget, _time, track);
-			//_history?.Commit(action);
 		}
 
 		private void tsDelete_Click(object sender, EventArgs e)
 		{
 			if (this.ContainsActiveControl())
 			{
-				if (_selectedWidget == null) { return; }
+				if (_selectedObject == null) { return; }
 				WidgetOperationArgs args = GetOperationArgs();
-				_selectedWidget.OnDelete(args);
+				_selectedObject.OnDelete(args);
 			}
 			else
 			{
@@ -1356,12 +1592,12 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 
 		private void tsPrevious_Click(object sender, EventArgs e)
 		{
-			_selectedWidget?.AdvanceSubWidget(false);
+			_selectedObject?.AdvanceSubWidget(false);
 		}
 
 		private void tsNext_Click(object sender, EventArgs e)
 		{
-			_selectedWidget?.AdvanceSubWidget(true);
+			_selectedObject?.AdvanceSubWidget(true);
 		}
 
 		private void ZoomOut()
@@ -1428,7 +1664,7 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			}
 			_contextMenuItems.Clear();
 			ContextMenuArgs args = new ContextMenuArgs();
-			_selectedWidget?.OnOpeningContextMenu(args);
+			_selectedObject?.OnOpeningContextMenu(args);
 			foreach (ContextMenuItem item in args.ItemsToAdd)
 			{
 				ToolStripMenuItem menuItem = new ToolStripMenuItem(item.Text, item.Icon, item.Click);
@@ -1463,6 +1699,24 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			Looping,
 			OnceLooping,
 		}
+
+		public void RequestUI(object data)
+		{
+			UIRequested?.Invoke(this, data);
+		}
+
+		private ITimelineBreak GetBreakBetween(float start, float end)
+		{
+			for (int i = 0; i < _breaks.Count; i++)
+			{
+				float time = _breaks[i].Time;
+				if (time > start && time <= end)
+				{
+					return _breaks[i];
+				}
+			}
+			return null;
+		}
 	}
 
 	public class ContextMenuItem
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/CameraWidget.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/CameraWidget.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d9442769dd6c5b91c068250bc290c2135876ad64
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/CameraWidget.cs	
@@ -0,0 +1,28 @@
+using System.Drawing;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class CameraWidget : KeyframedWidget
+	{
+		protected override Brush GetFillBrush(bool selected)
+		{
+			return selected ? Brushes.LightGray : Brushes.Gray;
+		}
+
+		protected override Brush GetTitleBrush()
+		{
+			return Brushes.DarkGray;
+		}
+
+		public CameraWidget(LiveAnimatedObject data, Timeline timeline) : base(data, timeline)
+		{
+			AllowDelete = false;
+			IsCollapsed = true;
+		}
+
+		public override Image GetThumbnail()
+		{
+			return Properties.Resources.VideoCamera;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/EmitterWidget.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/EmitterWidget.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4b638e8e344e61974dd9db6214339b11a1acca23
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/EmitterWidget.cs	
@@ -0,0 +1,108 @@
+using System.ComponentModel;
+using System.Drawing;
+using Desktop.Skinning;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class EmitterWidget : KeyframedWidget
+	{
+		private const int ThumbnailHeight = 32;
+
+		private static SolidBrush _titleFill;
+		private static SolidBrush _subrowFill;
+		private static SolidBrush _selectedSubrowFill;
+
+		public LiveEmitter Emitter { get; private set; }
+
+		private Image _thumbnail;
+
+		protected override void OnSetData(LiveAnimatedObject data)
+		{
+			Emitter = data as LiveEmitter;
+		}
+
+		static EmitterWidget()
+		{
+			_titleFill = new SolidBrush(Color.White);
+			_subrowFill = new SolidBrush(Color.White);
+			_selectedSubrowFill = new SolidBrush(Color.White);
+			SetEmitterSkin(SkinManager.Instance.CurrentSkin);
+		}
+
+		protected override void OnSetDefaultColors()
+		{
+			_titleFill.Color = Color.FromArgb(153, 197, 255);
+			_subrowFill.Color = Color.FromArgb(203, 206, 216);
+			_selectedSubrowFill.Color = Color.FromArgb(223, 226, 236);
+		}
+
+		protected override void OnUpdateSkin(Skin skin)
+		{
+			SetEmitterSkin(skin);
+		}
+
+		private static void SetEmitterSkin(Skin skin)
+		{
+			_titleFill.Color = skin.GetAppColor("WidgetHeaderRow");
+			_subrowFill.Color = skin.GetAppColor("WidgetRow");
+			_selectedSubrowFill.Color = skin.GetAppColor("WidgetRowSelected");
+		}
+
+		public EmitterWidget(LiveEmitter emitter, Timeline timeline) : base(emitter, timeline)
+		{
+			_timeline = timeline;
+			Emitter = emitter;
+			Emitter.Widget = this;
+			AllowParenting = false;
+		}
+
+		public override string ToString()
+		{
+			return string.IsNullOrEmpty(Emitter.Id) ? "Empty Emitter" : Emitter.Id;
+		}
+
+		protected override void OnDataPropertyChanged(object sender, PropertyChangedEventArgs e)
+		{
+			if (e.PropertyName == "Keyframes")
+			{
+				_thumbnail?.Dispose();
+				_thumbnail = null;
+			}
+		}
+
+		protected override Brush GetFillBrush(bool selected)
+		{
+			return selected ? _selectedSubrowFill : _subrowFill;
+		}
+
+		protected override Brush GetTitleBrush()
+		{
+			return _titleFill;
+		}
+
+		public override Image GetThumbnail()
+		{
+			if (_thumbnail == null && Data.Properties.Contains("Src"))
+			{
+				string src = Data.GetPropertyValue<string>("Src", 0, 0, null);
+				if (!string.IsNullOrEmpty(src))
+				{
+					try
+					{
+						Image bmp = LiveImageCache.Get(src);
+
+						//create a 32px tall image matching the source's aspect ratio
+						int width = (int)((float)bmp.Width / bmp.Height * ThumbnailHeight);
+						_thumbnail = new Bitmap(width, ThumbnailHeight);
+						using (Graphics g = Graphics.FromImage(_thumbnail))
+						{
+							g.DrawImage(bmp, 0, 0, _thumbnail.Width, _thumbnail.Height);
+						}
+					}
+					catch { }
+				}
+			}
+			return _thumbnail;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/KeyframedWidget.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/KeyframedWidget.cs
new file mode 100644
index 0000000000000000000000000000000000000000..afc20675d65d9bca7da45a62a405fcc9251bd323
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/KeyframedWidget.cs	
@@ -0,0 +1,1190 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+using Desktop;
+using Desktop.Skinning;
+using SPNATI_Character_Editor.Actions;
+using SPNATI_Character_Editor.Actions.TimelineActions;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	/// <summary>
+	/// Base class for any widget that can hold keyframes
+	/// </summary>
+	public abstract class KeyframedWidget : ITimelineWidget
+	{
+		protected bool _selected;
+		protected bool _collapsed = false;
+
+		protected bool AllowParenting = false;
+		protected bool AllowDelete = true;
+		protected bool ShowPropertyRows = true;
+
+		private static SolidBrush _repeatFill;
+		private static Dictionary<string, Image> _easeIcons;
+		private static Dictionary<string, Image> _tweenIcons;
+		private static SolidBrush _headerKeyframeFill;
+		private static SolidBrush _keyframeFill;
+		private static SolidBrush _keyframeFillSelected;
+		private static Pen _penKeyframe;
+
+		private const int KeyframeRadius = 5;
+
+		protected Timeline _timeline;
+
+		public event EventHandler Invalidated;
+		private LiveKeyframe _selectedFrame;
+		private HashSet<string> _selectedProperties = new HashSet<string>();
+		private LiveKeyframe _hoverFrame;
+		private int _hoverRow;
+		private bool _playing;
+
+		private LiveAnimatedObject _data;
+		public LiveAnimatedObject Data
+		{
+			get { return _data; }
+			private set
+			{
+				_data = value;
+				OnSetData(value);
+			}
+		}
+		protected virtual void OnSetData(LiveAnimatedObject data)
+		{
+
+		}
+
+		static KeyframedWidget()
+		{
+			_headerKeyframeFill = new SolidBrush(Color.FromArgb(255, 226, 66));
+			_keyframeFill = new SolidBrush(Color.FromArgb(180, 180, 180));
+			_keyframeFillSelected = new SolidBrush(Color.FromArgb(245, 245, 255));
+			_repeatFill = new SolidBrush(Color.FromArgb(103, 106, 116));
+
+			_penKeyframe = new Pen(Color.Black);
+
+			_easeIcons = new Dictionary<string, Image>();
+			_easeIcons["linear"] = Properties.Resources.Curve_Linear;
+			_easeIcons["smooth"] = Properties.Resources.Curve_Smooth;
+			_easeIcons["ease-in"] = Properties.Resources.Curve_EaseIn;
+			_easeIcons["ease-out"] = Properties.Resources.Curve_EaseOut;
+			_easeIcons["ease-in-cubic"] = Properties.Resources.Curve_EaseInCubic;
+			_easeIcons["ease-out-cubic"] = Properties.Resources.Curve_EaseOutCubic;
+			_easeIcons["ease-in-sin"] = Properties.Resources.Curve_EaseInSin;
+			_easeIcons["ease-out-sin"] = Properties.Resources.Curve_EaseOutSin;
+			_easeIcons["ease-in-out-cubic"] = Properties.Resources.Curve_EaseInOutCubic;
+			_easeIcons["ease-out-in"] = Properties.Resources.Curve_EaseOutIn;
+			_easeIcons["ease-out-in-cubic"] = Properties.Resources.Curve_EaseOutInCubic;
+			_easeIcons["elastic"] = Properties.Resources.Curve_Elastic;
+			_easeIcons["bounce"] = Properties.Resources.Curve_Bounce;
+
+			_tweenIcons = new Dictionary<string, Image>();
+			_tweenIcons["linear"] = Properties.Resources.Tween_Linear;
+			_tweenIcons["spline"] = Properties.Resources.Tween_Spline;
+			_tweenIcons["none"] = Properties.Resources.Tween_None;
+
+			SetSkin(SkinManager.Instance.CurrentSkin);
+		}
+
+		public void UpdateSkin(Skin skin)
+		{
+			SetSkin(skin);
+			OnUpdateSkin(skin);
+		}
+		protected virtual void OnUpdateSkin(Skin skin)
+		{
+		}
+
+		private static void SetDefaultColors()
+		{
+			_headerKeyframeFill.Color = Color.FromArgb(255, 226, 66);
+			_keyframeFill.Color = Color.FromArgb(180, 180, 180);
+			_keyframeFillSelected.Color = Color.FromArgb(245, 245, 255);
+			_repeatFill.Color = Color.FromArgb(103, 106, 116);
+			_penKeyframe.Color = Color.Black;
+		}
+		protected virtual void OnSetDefaultColors()
+		{
+
+		}
+
+		public static void SetSkin(Skin skin)
+		{
+			if (!skin.AppColors.ContainsKey("WidgetHeaderRow"))
+			{
+				SetDefaultColors();
+				return;
+			}
+			_headerKeyframeFill.Color = skin.GetAppColor("KeyframeHeader");
+			_keyframeFill.Color = skin.GetAppColor("Keyframe0");
+			_penKeyframe.Color = skin.GetAppColor("KeyframeBorder");
+			_keyframeFillSelected.Color = skin.GetAppColor("KeyframeSelected");
+			_repeatFill.Color = skin.GetAppColor("WidgetRepeat");
+		}
+
+		public KeyframedWidget(LiveAnimatedObject data, Timeline timeline)
+		{
+			_timeline = timeline;
+			Data = data;
+			data.PropertyChanged += Data_PropertyChanged;
+			data.Keyframes.CollectionChanged += Keyframes_CollectionChanged;
+		}
+
+		private void Keyframes_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+		{
+			if (_selected)
+			{
+				if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
+				{
+					LiveKeyframe kf = e.NewItems[0] as LiveKeyframe;
+					SelectKeyframe(kf, null, false);
+					_timeline.SelectData(kf);
+				}
+			}
+		}
+
+		public void AdvanceSubWidget(bool forward)
+		{
+			if (Data.Keyframes.Count > 1)
+			{
+				int index = 0;
+				if (_selectedFrame != null)
+				{
+					index = Data.Keyframes.IndexOf(_selectedFrame);
+					if (forward)
+					{
+						index++;
+						if (index >= Data.Keyframes.Count)
+						{
+							index = 0;
+						}
+					}
+					else
+					{
+						index--;
+						if (index < 0)
+						{
+							index = Data.Keyframes.Count - 1;
+						}
+					}
+				}
+				LiveKeyframe kf = Data.Keyframes[index];
+				SelectKeyframe(kf, null, false);
+				_timeline.CurrentTime = kf.Time + Data.Start;
+				SelectFrameDataWithPreview(_timeline.CurrentTime);
+			}
+			else
+			{
+				if (forward)
+				{
+					_timeline.CurrentTime = Data.Start + Data.Length;
+					SelectFrameDataWithPreview(_timeline.CurrentTime);
+				}
+				else
+				{
+					LiveKeyframe kf = Data.Keyframes[0];
+					SelectKeyframe(kf, null, false);
+					_timeline.CurrentTime = kf.Time + Data.Start;
+					SelectFrameDataWithPreview(_timeline.CurrentTime);
+				}
+			}
+		}
+
+		public void OnWidgetSelectionChanged(WidgetSelectionArgs args)
+		{
+			_timeline = args.Timeline;
+			//if (args.Modifiers.HasFlag(Keys.Control))
+			//{
+			//	return;
+			//}
+			_selected = (args.IsSelected != SelectionType.Deselect);
+			if (args.IsSelected != SelectionType.Select)
+			{
+				_hoverFrame = null;
+				ClearSelection();
+			}
+			else
+			{
+				float time = args.Timeline.CurrentTime;
+				if (Data == null || Data.Data is LiveScene)
+				{
+					SelectFrameDataWithPreview(time);
+				}
+				else
+				{
+					LiveKeyframe kf = Data.Keyframes.Find(k => k.Time == time);
+					if (kf == null)
+					{
+						args.Timeline.SelectData(Data);
+					}
+					else
+					{
+						SelectKeyframe(kf, null, false);
+						args.Timeline.SelectData(kf);
+					}
+				}
+			}
+		}
+
+		public void OnPlaybackChanged(bool playing)
+		{
+			_playing = playing;
+		}
+
+		public object GetData()
+		{
+			return Data;
+		}
+
+		protected void Invalidate()
+		{
+			Invalidated?.Invoke(this, EventArgs.Empty);
+		}
+
+		private void Data_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			OnDataPropertyChanged(sender, e);
+			Invalidate();
+		}
+		protected virtual void OnDataPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+
+		}
+
+		public int GetRowCount()
+		{
+			return !ShowPropertyRows || _collapsed ? 1 : Data.AnimatedProperties.Count + 1;
+		}
+
+		public float GetStart()
+		{
+			return Data.Start;
+		}
+		public void SetStart(float time)
+		{
+			Data.Start = time;
+		}
+
+		public float GetLength(float duration)
+		{
+			if (duration != 0)
+			{
+				return duration - GetStart();
+			}
+			else
+			{
+				return Data.Length;
+			}
+		}
+
+		private Brush GetFillBrush()
+		{
+			return GetFillBrush(_selected);
+		}
+		protected abstract Brush GetFillBrush(bool selected);
+		protected abstract Brush GetTitleBrush();
+
+		private Pen GetOutline()
+		{
+			//return _selected ? Timeline.WidgetSelectedOutline : Timeline.WidgetOutline;
+			return Timeline.WidgetOutline;
+		}
+
+		public void ClearSelection()
+		{
+			_selectedFrame = null;
+			_selectedProperties.Clear();
+			Invalidate();
+		}
+
+		public List<string> SelectedProperties
+		{
+			get
+			{
+				List<string> properties = new List<string>();
+				foreach (string property in _selectedProperties)
+				{
+					properties.Add(property);
+				}
+				return properties;
+			}
+		}
+
+		public LiveKeyframe SelectedFrame
+		{
+			get { return _selectedFrame; }
+		}
+
+		public void SelectKeyframe(LiveKeyframe keyframe, string property, bool addToSelection)
+		{
+			Invalidate();
+			if (_selectedFrame != keyframe || property == null)
+			{
+				_selectedProperties.Clear();
+			}
+			else if (_selectedFrame != null && _selectedProperties.Count == 0 && property != null)
+			{
+				//selecting an individual property after selecting the whole frame - ignore
+				return;
+			}
+			else
+			{
+				bool alreadySelected = _selectedProperties.Contains(property);
+				if (alreadySelected)
+				{
+					if (addToSelection)
+					{
+						_selectedProperties.Remove(property);
+						return;
+					}
+				}
+				else
+				{
+					if (!addToSelection)
+					{
+						_selectedProperties.Clear();
+					}
+				}
+			}
+			_selectedFrame = keyframe;
+			if (property != null)
+			{
+				_selectedProperties.Add(property);
+			}
+		}
+
+		private int GetStandardIconCount()
+		{
+			if (AllowParenting)
+			{
+				return 2;
+			}
+			else
+			{
+				return 1;
+			}
+		}
+
+		public int GetHeaderIconCount(int row)
+		{
+			if (row == 0)
+			{
+				return GetStandardIconCount() + GetExtraHeaderIconCount();
+			}
+			else
+			{
+				return 3;
+			}
+		}
+
+		public void DrawHeaderIcon(Graphics g, int rowIndex, int iconIndex, int x, int y, int iconWidth, int highlightedIconIndex)
+		{
+			Image icon = null;
+			if (rowIndex == 0)
+			{
+				int count = GetStandardIconCount();
+				if (iconIndex < count)
+				{
+					switch (iconIndex)
+					{
+						case 0:
+							icon = Data.Hidden ? Properties.Resources.EyeClosed : Properties.Resources.EyeOpen;
+							break;
+						case 1:
+							if (AllowParenting)
+							{
+								if (Data.Parent != null)
+								{
+									icon = Data.Parent.Image;
+								}
+								else
+								{
+									icon = Properties.Resources.AddLink;
+								}
+							}
+							break;
+					}
+				}
+				else
+				{
+					icon = GetExtraHeaderIcon(iconIndex - GetStandardIconCount());
+				}
+			}
+			else
+			{
+				string property = Data.Properties[rowIndex - 1];
+				LiveKeyframeMetadata metadata = Data.GetBlockMetadata(property, _timeline.CurrentTime);
+				switch (iconIndex)
+				{
+					case 0:
+						if (highlightedIconIndex == 0)
+						{
+							icon = Properties.Resources.Loop;
+						}
+						else
+						{
+							icon = metadata.Looped ? Properties.Resources.Loop : null;
+						}
+						break;
+					case 1:
+						string tween = metadata.Interpolation ?? "none";
+						if (!_tweenIcons.TryGetValue(tween, out icon))
+						{
+							icon = _tweenIcons["none"];
+						}
+						break;
+					case 2:
+						string ease = metadata.Ease ?? "smooth";
+						if (!_easeIcons.TryGetValue(ease, out icon))
+						{
+							icon = _easeIcons["smooth"];
+						}
+						break;
+				}
+			}
+			if (icon != null)
+			{
+				g.DrawImage(icon, x, y, iconWidth, iconWidth);
+			}
+		}
+
+		protected virtual int GetExtraHeaderIconCount() { return 0; }
+		protected virtual Image GetExtraHeaderIcon(int iconIndex) { return null; }
+		protected virtual string GetExtraHeaderTooltip(WidgetActionArgs args, int iconIndex) { return ""; }
+
+		private void DrawBlock(Graphics g, LiveKeyframe start, LiveKeyframe end, int y, float pps, int rowHeight)
+		{
+			DrawBlock(g, start.Time, end.Time, y, pps, rowHeight);
+		}
+
+		private void DrawBlock(Graphics g, float start, float end, int y, float pps, int rowHeight)
+		{
+			float startX = (Data.Start + start) * pps;
+			float length = (end - start) * pps;
+			g.FillRectangle(GetFillBrush(), startX, y, length, rowHeight + 1);
+			Pen outline = GetOutline();
+			g.DrawLine(outline, startX, y, startX, y + rowHeight);
+			g.DrawLine(outline, startX + length, y, startX + length, y + rowHeight);
+		}
+
+		public void DrawContents(Graphics g, int rowIndex, int x, int y, float pps, int rowHeight)
+		{
+			if (rowIndex == 0)
+			{
+				float length = Data.Keyframes[Data.Keyframes.Count - 1].Time * pps;
+				if (Data.Keyframes.Count == 1)
+				{
+					length = Data.Length * pps;
+				}
+				float startX = Data.Start * pps;
+				g.FillRectangle(GetTitleBrush(), startX, y, length, rowHeight + 1);
+				g.DrawLine(Timeline.WidgetOutline, startX, y, startX, y + rowHeight);
+				g.DrawLine(Timeline.WidgetOutline, startX + length, y, startX + length, y + rowHeight);
+
+				//global keyframes
+				foreach (LiveKeyframe kf in Data.Keyframes)
+				{
+					DrawKeyframe(g, (kf == _selectedFrame && _selectedProperties.Count == 0) || (kf == _hoverFrame && _hoverRow == 0) ? _keyframeFillSelected : _headerKeyframeFill, TimeToX(Data.Start + kf.Time, pps), y + rowHeight / 2 - KeyframeRadius - 1, kf, -1, null);
+				}
+			}
+			else
+			{
+				string property = Data.Properties[rowIndex - 1];
+
+				if (Data.Keyframes.Count == 1)
+				{
+					DrawBlock(g, 0, Data.Length, y, pps, rowHeight);
+				}
+				else
+				{
+					//fill blocks of contiguous animation
+					LiveKeyframe startFrame = null;
+					LiveKeyframe lastFrame = null;
+					for (int i = 0; i < Data.Keyframes.Count; i++)
+					{
+						LiveKeyframe kf = Data.Keyframes[i];
+						if (kf.HasProperty(property))
+						{
+							KeyframeType type = kf.GetFrameType(property);
+							if (startFrame == null)
+							{
+								startFrame = kf;
+								lastFrame = kf;
+							}
+							else if (type == KeyframeType.Split)
+							{
+								//found the end frame
+								DrawBlock(g, startFrame, kf, y, pps, rowHeight);
+								startFrame = kf;
+								lastFrame = kf;
+							}
+							else if (type == KeyframeType.Begin)
+							{
+								//found the end frame
+								DrawBlock(g, startFrame, lastFrame, y, pps, rowHeight);
+								startFrame = kf;
+							}
+							else
+							{
+								lastFrame = kf;
+							}
+						}
+					}
+					if (startFrame != null && startFrame != Data.Keyframes[Data.Keyframes.Count - 1])
+					{
+						DrawBlock(g, startFrame, Data.Keyframes[Data.Keyframes.Count - 1], y, pps, rowHeight);
+					}
+				}
+
+				if (_playing)
+				{
+					float start, end;
+					float time = Data.GetInterpolatedTime(property, Data.Time, Data.TimeOffset, null, null, out start, out end);
+					int right = TimeToX(end + Data.Start, pps);
+					int left = TimeToX(start + Data.Start, pps);
+					int width = (right - left);
+					int interpolatedX = left + (int)(width * time);
+					g.FillRectangle(Brushes.DarkBlue, interpolatedX - 1, y, 3, rowHeight);
+				}
+
+				HashSet<LiveKeyframe> drawnBlocks = new HashSet<LiveKeyframe>();
+				for (int i = 0; i < Data.Keyframes.Count; i++)
+				{
+					LiveKeyframe kf = Data.Keyframes[i];
+					if (!kf.HasProperty(property)) { continue; }
+
+					//repeat sign if this is the last frame of a looped block
+					LiveKeyframe start, end;
+					Data.GetBlock(property, kf.Time, out start, out end);
+					if (start != null)
+					{
+						if (!drawnBlocks.Contains(start))
+						{
+							drawnBlocks.Add(start);
+							LiveKeyframeMetadata metadata = start.GetMetadata(property, false);
+							if (metadata != null && metadata.Looped)
+							{
+								int repeatX = TimeToX(Data.Start + end.Time, pps);
+								g.FillEllipse(_repeatFill, repeatX + 6, y + rowHeight / 3 - 2, 4, 4);
+								g.FillEllipse(_repeatFill, repeatX + 6, y + 2 * rowHeight / 3 - 2, 4, 4);
+								g.FillRectangle(_repeatFill, repeatX + 11, y, 1, rowHeight + 1);
+								g.FillRectangle(_repeatFill, repeatX + 13, y, 3, rowHeight + 1);
+							}
+						}
+					}
+
+					//keyframe
+					DrawKeyframe(g, (kf == _selectedFrame && (_selectedProperties.Count == 0 || _selectedProperties.Contains(property))) || (kf == _hoverFrame && (_hoverRow == 0 || _hoverRow == rowIndex)) ? _keyframeFillSelected : _keyframeFill, TimeToX(Data.Start + kf.Time, pps), y + rowHeight / 2 - KeyframeRadius - 1, kf, i, property);
+				}
+			}
+		}
+
+		private void DrawKeyframe(Graphics g, Brush brush, int x, int y, LiveKeyframe kf, int frameIndex, string property)
+		{
+			y += KeyframeRadius + 1;
+			Point[] pts;
+			KeyframeType type = kf.GetFrameType(property);
+			LiveKeyframe nextFrame = null;
+			if (property != null)
+			{
+				for (int i = frameIndex + 1; i < Data.Keyframes.Count; i++)
+				{
+					LiveKeyframe next = Data.Keyframes[i];
+					if (next.HasProperty(property))
+					{
+						nextFrame = next;
+						break;
+					}
+				}
+			}
+			if (property != null && type == KeyframeType.Begin)
+			{
+				// >
+				pts = new Point[] { new Point(x, y - KeyframeRadius), new Point(x + KeyframeRadius, y), new Point(x, y + KeyframeRadius) };
+			}
+			else if (property != null && frameIndex > 0 && nextFrame != null && nextFrame.GetFrameType(property) == KeyframeType.Begin)
+			{
+				// <
+				pts = new Point[] { new Point(x, y - KeyframeRadius), new Point(x, y + KeyframeRadius), new Point(x - KeyframeRadius, y) };
+			}
+			else if (property != null && type == KeyframeType.Split)
+			{
+				// <|>
+				pts = new Point[] { new Point(x, y - KeyframeRadius), new Point(x + KeyframeRadius, y), new Point(x, y + KeyframeRadius), new Point(x - KeyframeRadius, y), new Point(x, y - KeyframeRadius), new Point(x, y + KeyframeRadius) };
+			}
+			else
+			{
+				// <>
+				pts = new Point[] { new Point(x - KeyframeRadius, y), new Point(x, y - KeyframeRadius), new Point(x + KeyframeRadius, y), new Point(x, y + KeyframeRadius) };
+			}
+			g.FillPolygon(brush, pts);
+			g.DrawPolygon(_penKeyframe, pts);
+		}
+
+		protected int TimeToX(float time, float pps)
+		{
+			return (int)(time * pps);
+		}
+
+		public virtual string GetLabel(int row)
+		{
+			if (row == 0)
+			{
+				return Data.Id;
+			}
+			else
+			{
+				string property = Data.Properties[row - 1];
+				PropertyDefinition definition = Definitions.Instance.Get<PropertyDefinition>(property);
+				if (definition != null)
+				{
+					return definition.Name;
+				}
+				return "Unknown property";
+			}
+		}
+
+		public virtual Image GetThumbnail() { return null; }
+
+		public string GetHeaderTooltip(WidgetActionArgs args, int iconIndex)
+		{
+			if (args.Row > 0)
+			{
+				string property = Data.Properties[args.Row - 1];
+				LiveKeyframeMetadata metadata = Data.GetBlockMetadata(property, _timeline.CurrentTime);
+				switch (iconIndex)
+				{
+					case 0:
+						return "Toggle looping";
+					case 1:
+						return $"Tweening: {metadata.Interpolation ?? "none"}";
+					case 2:
+						return $"Easing method: {metadata.Ease ?? "smooth"}";
+				}
+			}
+			else
+			{
+				if (iconIndex < GetStandardIconCount())
+				{
+					switch (iconIndex)
+					{
+						case 0:
+							return "Toggle visibility";
+						case 1:
+							return AllowParenting ? (Data.Parent == null ? "Unlinked" : $"Linked to: {Data.ParentId}") : "";
+					}
+				}
+				else
+				{
+					return GetExtraHeaderTooltip(args, iconIndex - GetStandardIconCount());
+				}
+			}
+			return null;
+		}
+
+		public void OnClickHeaderIcon(WidgetActionArgs args, int iconIndex)
+		{
+			if (args.Row == 0)
+			{
+				if (iconIndex < GetStandardIconCount())
+				{
+					switch (iconIndex)
+					{
+						case 0:
+							Data.Hidden = !Data.Hidden;
+							break;
+						case 1:
+							if (AllowParenting)
+							{
+								List<LiveSprite> sprites = new List<LiveSprite>();
+								LivePose pose = Data.Data as LivePose;
+								foreach (LiveSprite sprite in pose.Sprites)
+								{
+									if (string.IsNullOrEmpty(sprite.Id) || sprite == Data)
+									{
+										continue;
+									}
+									//if this is an ancestor of the sprite, disallow it to avoid infinite chains
+									LiveObject parent = sprite.Parent;
+									bool isAncestor = false;
+									while (parent != null)
+									{
+										if (parent == Data)
+										{
+											isAncestor = true;
+											break;
+										}
+										parent = parent.Parent;
+									}
+									if (!isAncestor)
+									{
+										sprites.Add(sprite);
+									}
+								}
+								sprites.Sort();
+								ContextMenuItem[] items = new ContextMenuItem[sprites.Count + 1];
+								items[0] = new ContextMenuItem("Unlinked", null, SelectParent, null, Data.Parent == null);
+								for (int i = 0; i < sprites.Count; i++)
+								{
+									LiveSprite sprite = sprites[i];
+									items[i + 1] = new ContextMenuItem(sprite.Id, sprite.Image, SelectParent, sprite.Id, Data.Parent == sprite);
+								}
+								args.Timeline.ShowContextMenu(items);
+							}
+							break;
+					}
+				}
+				else
+				{
+					OnClickExtraHeaderIcon(args, iconIndex - GetStandardIconCount());
+				}
+			}
+			else
+			{
+				string property = Data.Properties[args.Row - 1];
+				LiveKeyframeMetadata metadata = Data.GetBlockMetadata(property, args.Time);
+				switch (iconIndex)
+				{
+					case 0:
+						ToggleLooping(property, metadata, !metadata.Looped);
+						Invalidate();
+						break;
+					case 1:
+						string tween = metadata.Interpolation;
+						args.Timeline.ShowContextMenu(
+							new ContextMenuItem("Linear", Properties.Resources.Tween_Linear, SelectTween, new Tuple<string, string>(property, "linear"), tween == "linear"),
+							new ContextMenuItem("Spline", Properties.Resources.Tween_Spline, SelectTween, new Tuple<string, string>(property, "spline"), tween == "spline"),
+							new ContextMenuItem("None", Properties.Resources.Tween_None, SelectTween, new Tuple<string, string>(property, "none"), tween == "none" || string.IsNullOrEmpty(tween))
+							);
+						break;
+					case 2:
+						string ease = metadata.Ease;
+						args.Timeline.ShowContextMenu(
+							new ContextMenuItem("Linear", Properties.Resources.Curve_Linear, SelectEase, new Tuple<string, string>(property, "linear"), ease == "linear"),
+							new ContextMenuItem("Smooth", Properties.Resources.Curve_Smooth, SelectEase, new Tuple<string, string>(property, "smooth"), ease == "smooth" || string.IsNullOrEmpty(ease)),
+							new ContextMenuItem("Ease-In-Out Cubic", Properties.Resources.Curve_EaseInOutCubic, SelectEase, new Tuple<string, string>(property, "ease-in-out-cubic"), ease == "ease-in-out-cubic"),
+							new ContextMenuItem("Ease-Out-In", Properties.Resources.Curve_EaseOutIn, SelectEase, new Tuple<string, string>(property, "ease-out-in"), ease == "ease-out-in"),
+							new ContextMenuItem("Ease-Out-In Cubic", Properties.Resources.Curve_EaseOutInCubic, SelectEase, new Tuple<string, string>(property, "ease-out-in-cubic"), ease == "ease-out-in-cubic"),
+							new ContextMenuItem("Ease-In", Properties.Resources.Curve_EaseIn, SelectEase, new Tuple<string, string>(property, "ease-in"), ease == "ease-in"),
+							new ContextMenuItem("Ease-In Sine", Properties.Resources.Curve_EaseInSin, SelectEase, new Tuple<string, string>(property, "ease-in-sin"), ease == "ease-in-sin"),
+							new ContextMenuItem("Ease-In Cubic", Properties.Resources.Curve_EaseInCubic, SelectEase, new Tuple<string, string>(property, "ease-in-cubic"), ease == "ease-in-cubic"),
+							new ContextMenuItem("Ease-Out", Properties.Resources.Curve_EaseOut, SelectEase, new Tuple<string, string>(property, "ease-out"), ease == "ease-out"),
+							new ContextMenuItem("Ease-Out Sine", Properties.Resources.Curve_EaseOutSin, SelectEase, new Tuple<string, string>(property, "ease-out-sin"), ease == "ease-out-sin"),
+							new ContextMenuItem("Ease-Out Cubic", Properties.Resources.Curve_EaseOutCubic, SelectEase, new Tuple<string, string>(property, "ease-out-cubic"), ease == "ease-out-cubic"),
+							new ContextMenuItem("Bounce", Properties.Resources.Curve_Bounce, SelectEase, new Tuple<string, string>(property, "bounce"), ease == "bounce"),
+							new ContextMenuItem("Elastic", Properties.Resources.Curve_Elastic, SelectEase, new Tuple<string, string>(property, "elastic"), ease == "elastic")
+							);
+						break;
+				}
+			}
+		}
+		protected virtual void OnClickExtraHeaderIcon(WidgetActionArgs args, int iconIndex) { }
+
+		private void ToggleLooping(string property, LiveKeyframeMetadata metadata, bool looped)
+		{
+			//TODO: make this an ICommand
+			LiveKeyframe kf = Data.GetBlockKeyframe(property, _timeline.CurrentTime);
+			kf.GetMetadata(property, true).Looped = looped;
+		}
+
+		private void SelectTween(object sender, EventArgs e)
+		{
+			//TODO: Make this an ICommand
+			ToolStripMenuItem item = sender as ToolStripMenuItem;
+			Tuple<string, string> tag = item.Tag as Tuple<string, string>;
+			string property = tag.Item1;
+			string tween = tag.Item2;
+
+			LiveKeyframe kf = Data.GetBlockKeyframe(property, _timeline.CurrentTime);
+			kf.GetMetadata(property, true).Interpolation = tween;
+		}
+
+		private void SelectEase(object sender, EventArgs e)
+		{
+			//TODO: Make this an ICommand
+			ToolStripMenuItem item = sender as ToolStripMenuItem;
+			Tuple<string, string> tag = item.Tag as Tuple<string, string>;
+			string property = tag.Item1;
+			string ease = tag.Item2;
+
+			LiveKeyframe kf = Data.GetBlockKeyframe(property, _timeline.CurrentTime);
+			kf.GetMetadata(property, true).Ease = ease;
+		}
+
+		private void SelectParent(object sender, EventArgs e)
+		{
+			//TODO: Make this an ICommand
+			ToolStripMenuItem item = sender as ToolStripMenuItem;
+			string id = item.Tag?.ToString();
+			Data.ParentId = id;
+		}
+
+		public void OnClickHeader(WidgetActionArgs args)
+		{
+			int row = args.Row;
+			if (row == 0)
+			{
+				ClearSelection();
+				args.Timeline.SelectData(Data);
+			}
+			else
+			{
+				bool add = args.Modifiers.HasFlag(Keys.Control);
+				if (!add)
+				{
+					ClearSelection();
+				}
+				_selectedFrame = null;
+				string property = Data.Properties[row - 1];
+				_selectedProperties.Add(property);
+				LiveKeyframeMetadata metadata = Data.GetBlockMetadata(property, args.Time);
+				args.Timeline.SelectData(metadata);
+				Invalidate();
+			}
+		}
+
+		public void OnDoubleClickHeader(WidgetActionArgs args)
+		{
+			//select first keyframe
+			SelectFrameDataWithPreview(0);
+		}
+
+		public ITimelineAction GetAction(int x, float start, int row, int timelineWidth, float pps)
+		{
+			_hoverFrame = null;
+
+			//see if a keyframe is selected
+			for (int i = Data.Keyframes.Count - 1; i >= 0; i--)
+			{
+				LiveKeyframe kf = Data.Keyframes[i];
+				int kfX = TimeToX(kf.Time, pps);
+				if (Math.Abs(x - kfX) <= 5)
+				{
+					_hoverFrame = kf;
+					_hoverRow = row;
+					string property = row > 0 ? Data.Properties[row - 1] : null;
+					if (string.IsNullOrEmpty(property) || kf.HasProperty(property))
+					{
+						return new SelectKeyframeTimelineAction(this, kf, property);
+					}
+				}
+			}
+
+			float end = Data.Length * pps;
+			if (Data.Keyframes.Count <= 1 && x > end - 5 && x <= end + 5)
+			{
+				return new ModifyWidgetLengthTimelineAction();
+			}
+			else if (x >= 5 && x <= end - 5)
+			{
+				return new MoveWidgetTimelineAction(true);
+			}
+			return null;
+		}
+
+		public void OnStartMove(WidgetActionArgs args)
+		{
+			ClearSelection();
+			if (Data == null || Data.Data is LivePose)
+			{
+				args.Timeline.SelectData(GetData());
+			}
+		}
+
+		public void OnMouseOut()
+		{
+			_hoverFrame = null;
+			Invalidate();
+		}
+
+		public void OnTimeChanged(WidgetOperationArgs args)
+		{
+			float time = args.Time;
+
+			if (_selected)
+			{
+				if (time < Data.Start)
+				{
+					args.Timeline.SelectData(Data);
+				}
+				else
+				{
+					SelectFrameDataWithPreview(time);
+				}
+			}
+		}
+
+		protected LiveKeyframe SelectFrameDataWithPreview(float time)
+		{
+			LiveKeyframe previewFrame = Data.GetInterpolatedFrame(time - Data.Start);
+
+			//show whatever keyframe is under the current time, or an interpolated placeholder if there is none
+			LiveKeyframe frame = Data.Keyframes.Find(kf => kf.Time == time);
+			if (frame == null)
+			{
+				frame = Data.CreateKeyframe(time - Data.Start);
+				frame.Data = Data;
+				frame.PropertyChanged += NewFrame_PropertyChanged;
+			}
+			_timeline.SelectData(frame, previewFrame);
+			return frame;
+		}
+
+		private void NewFrame_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			//the frame has a real property now, so add it to the sprite
+			LiveKeyframe frame = sender as LiveKeyframe;
+			frame.PropertyChanged -= NewFrame_PropertyChanged;
+			Data.AddKeyframe(frame);
+			_timeline.SelectData(frame);
+		}
+
+		public bool IsCollapsible
+		{
+			get { return ShowPropertyRows && Data.AnimatedProperties.Count > 0; }
+		}
+
+		public bool IsCollapsed
+		{
+			get { return _collapsed; }
+			set { _collapsed = value; }
+		}
+
+		public bool IsRowHighlighted(int row)
+		{
+			string property = row > 0 ? Data.Properties[row - 1] : null;
+			return row == 0 ? _selectedProperties.Count == 0 : _selectedProperties.Contains(property);
+		}
+
+		public void UpdateSelection(WidgetSelectionArgs args)
+		{
+			if (_selectedFrame != null)
+			{
+				//keyframe selected
+				if (!Data.Keyframes.Contains(_selectedFrame))
+				{
+					ClearSelection();
+				}
+				else if (_selectedProperties.Count > 0)
+				{
+					List<string> rows = new List<string>();
+					foreach (string property in _selectedProperties)
+					{
+						rows.Add(property);
+					}
+					foreach (string row in rows)
+					{
+						if (!_selectedFrame.HasProperty(row))
+						{
+							_selectedProperties.Remove(row);
+						}
+					}
+					if (_selectedProperties.Count == 0)
+					{
+						_selectedFrame = null;
+					}
+				}
+				if (_selectedFrame != null)
+				{
+					args.AllowCut = true;
+					args.AllowCopy = true;
+					args.AllowDelete = _selectedFrame.Time > 0 || _selectedProperties.Count > 0;
+					args.AllowDuplicate = true;
+				}
+			}
+			if (_selectedFrame == null)
+			{
+				if (_selectedProperties.Count == 0)
+				{
+					//widget selected
+					args.AllowCut = true;
+					args.AllowCopy = true;
+					args.AllowDelete = true;
+					args.AllowDuplicate = true;
+				}
+				else
+				{
+					//one or more rows are selected
+					args.AllowCut = true;
+					args.AllowCopy = true;
+					args.AllowDelete = true;
+					args.AllowDuplicate = false;
+				}
+			}
+
+			args.AllowPaste = false;
+			object clipboardData = Clipboards.Get<KeyframedWidget, object>();
+			if (clipboardData is LiveSprite)
+			{
+				args.AllowPaste = true;
+			}
+			else if (clipboardData is LiveKeyframe)
+			{
+				args.AllowPaste = args.Timeline.CurrentTime >= Data.Start;
+			}
+		}
+
+		public bool OnCopy(WidgetOperationArgs args)
+		{
+			if (_selectedFrame != null)
+			{
+				//copy a frame
+				LiveKeyframe kf = Data.CopyKeyframe(_selectedFrame, _selectedProperties);
+				Clipboards.Set<KeyframedWidget>(kf);
+				return true;
+			}
+			else
+			{
+				//copy the whole widget
+				LiveAnimatedObject sprite = Data.Copy() as LiveAnimatedObject;
+				Clipboards.Set<KeyframedWidget>(sprite);
+				return true;
+			}
+		}
+
+		public bool OnDelete(WidgetOperationArgs args)
+		{
+			if (_selectedFrame != null)
+			{
+				if (_selectedProperties.Count == 0)
+				{
+					DeleteKeyframeCommand command = new DeleteKeyframeCommand(Data, _selectedFrame);
+					args.History.Commit(command);
+				}
+				else
+				{
+					args.History.StartBulkRecord();
+					foreach (string property in _selectedProperties)
+					{
+						DeletePropertyCommand command = new DeletePropertyCommand(Data, _selectedFrame, property);
+						command.Do();
+						args.History.Record(command);
+					}
+					args.History.EndBulkRecord();
+				}
+			}
+			else if (_selectedProperties.Count > 0)
+			{
+				//delete properties
+				args.History.StartBulkRecord();
+				foreach (string property in _selectedProperties)
+				{
+					DeleteAnimatedPropertyCommand command = new DeleteAnimatedPropertyCommand(Data, property);
+					command.Do();
+					args.History.Record(command);
+				}
+				args.History.EndBulkRecord();
+			}
+			else
+			{
+				//delete whole widget
+				if (AllowDelete && (args.IsSilent || MessageBox.Show($"Are you sure you want to completely remove {ToString()}?", "Remove Sprite", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes))
+				{
+					DeleteWidgetCommand command = new DeleteWidgetCommand(Data.Data, this);
+					args.History.Commit(command);
+					args.Timeline.SelectData(null);
+				}
+			}
+			return true;
+		}
+
+		public bool OnPaste(WidgetOperationArgs args)
+		{
+			object clipboardData = Clipboards.Get<KeyframedWidget, object>();
+			if (clipboardData is LiveKeyframe)
+			{
+				//Pastes a copied frame into the current position, overwriting any properties in frame already there
+				LiveKeyframe copiedFrame = clipboardData as LiveKeyframe;
+				if (copiedFrame != null)
+				{
+					float time = args.Time;
+
+					//if a keyframe is nearby, paste into it
+					int x = args.Timeline.TimeToX(time);
+					float minTime = args.Timeline.XToTime(x - KeyframeRadius);
+					float maxTime = args.Timeline.XToTime(x + KeyframeRadius);
+					LiveKeyframe frame = Data.Keyframes.Find(k => minTime <= k.Time && maxTime >= k.Time);
+					if (frame != null)
+					{
+						time = frame.Time;
+					}
+
+					PasteKeyframeCommand command = new PasteKeyframeCommand(Data, copiedFrame, time);
+					args.History.Commit(command);
+					_selectedFrame = command.NewKeyframe;
+					_selectedProperties.Clear();
+					args.Timeline.SelectData(_selectedFrame);
+				}
+			}
+			else if (clipboardData is LiveAnimatedObject)
+			{
+				return Data.Data.Paste(args, Data);
+			}
+			return true;
+		}
+
+		public bool OnDuplicate(WidgetOperationArgs args)
+		{
+			if (_selectedFrame == null)
+			{
+				//duplicating whole widget
+				LiveObject sprite = Data.Copy();
+				object oldClipboard = Clipboards.Get<KeyframedWidget, object>();
+				Clipboards.Set<KeyframedWidget>(sprite);
+				Data.Data.Paste(args, Data);
+				Clipboards.Set<KeyframedWidget>(oldClipboard);
+				return true;
+			}
+			else
+			{
+				//duplicating keyframe
+				float time = args.Time;
+
+				//if a keyframe is nearby, paste into it
+				int x = args.Timeline.TimeToX(time);
+				float minTime = args.Timeline.XToTime(x - KeyframeRadius);
+				float maxTime = args.Timeline.XToTime(x + KeyframeRadius);
+				LiveKeyframe frame = Data.Keyframes.Find(k => minTime <= k.Time && maxTime >= k.Time);
+				if (frame != null)
+				{
+					time = frame.Time;
+				}
+				if (Math.Abs(_selectedFrame.Time + Data.Start - time) < 0.001f)
+				{
+					return false; //can't duplicate into itself
+				}
+
+				PasteKeyframeCommand command = new PasteKeyframeCommand(Data, _selectedFrame, time);
+				args.History.Commit(command);
+				_selectedFrame = command.NewKeyframe;
+				_selectedProperties.Clear();
+				args.Timeline.SelectData(_selectedFrame);
+				return true;
+			}
+		}
+
+		public void OnOpeningContextMenu(ContextMenuArgs args)
+		{
+			if (_selectedFrame != null)
+			{
+				args.ItemsToAdd.Add(new ContextMenuItem("Toggle Keyframe Type", Properties.Resources.SplitKeyframe, ToggleSplit, null, false));
+			}
+		}
+
+		private void ToggleSplit(object sender, EventArgs e)
+		{
+			if (_selectedFrame == null)
+			{
+				return;
+			}
+			ToggleKeyframeTypeCommand command = new ToggleKeyframeTypeCommand(Data, _selectedFrame, _selectedProperties);
+			_timeline.CommandHistory.Commit(command);
+		}
+
+		public void OnDoubleClick(WidgetActionArgs args)
+		{
+			SelectFrameDataWithPreview(args.Time);
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/SpriteWidget.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/SpriteWidget.cs
index 3376eabb3ea39f284090652aff6139876fe64c95..40f9a3a871519df5477c452319e5e619257fd21f 100644
--- a/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/SpriteWidget.cs	
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/SpriteWidget.cs	
@@ -1,466 +1,106 @@
-using System;
-using System.Collections.Generic;
+using System.ComponentModel;
 using System.Drawing;
-using Desktop;
-using SPNATI_Character_Editor.Actions;
-using SPNATI_Character_Editor.Actions.TimelineActions;
-using System.Windows.Forms;
+using Desktop.Skinning;
 
 namespace SPNATI_Character_Editor.EpilogueEditor
 {
-	public class SpriteWidget : ITimelineWidget
+	public class SpriteWidget : KeyframedWidget
 	{
-		private bool _selected;
-		private static Brush _titleFill;
-		private static Brush _headerKeyframeFill;
-		private static Brush _keyframeFill;
-		private static Brush _keyframeFillSelected;
-		private static Pen _penKeyframe;
-		private static Brush _subrowFill;
-		private static Brush _selectedSubrowFill;
-		private static Brush _repeatFill;
-		private static Dictionary<string, Image> _easeIcons;
-		private static Dictionary<string, Image> _tweenIcons;
-
-		private const int KeyframeRadius = 5;
 		private const int ThumbnailHeight = 32;
 
+		private static SolidBrush _titleFill;
+		private static SolidBrush _subrowFill;
+		private static SolidBrush _selectedSubrowFill;
+
 		public LiveSprite Sprite { get; private set; }
 
-		private bool _collapsed = false;
 		private Image _thumbnail;
 
-		private Timeline _timeline;
-		public event EventHandler Invalidated;
-		private LiveKeyframe _selectedFrame;
-		private HashSet<string> _selectedProperties = new HashSet<string>();
-		private LiveKeyframe _hoverFrame;
-		private int _hoverRow;
-		private bool _playing;
 
-		static SpriteWidget()
+		protected override void OnSetData(LiveAnimatedObject data)
 		{
-			_titleFill = new SolidBrush(Color.FromArgb(153, 197, 255));
-			_subrowFill = new SolidBrush(Color.FromArgb(203, 206, 216));
-			_selectedSubrowFill = new SolidBrush(Color.FromArgb(223, 226, 236));
-			_headerKeyframeFill = new SolidBrush(Color.FromArgb(255, 226, 66));
-			_keyframeFill = new SolidBrush(Color.FromArgb(180, 180, 180));
-			_keyframeFillSelected = new SolidBrush(Color.FromArgb(245, 245, 255));
-			_repeatFill = new SolidBrush(Color.FromArgb(103, 106, 116));
-
-			_penKeyframe = Pens.Black;
-
-			_easeIcons = new Dictionary<string, Image>();
-			_easeIcons["linear"] = Properties.Resources.Curve_Linear;
-			_easeIcons["smooth"] = Properties.Resources.Curve_Smooth;
-			_easeIcons["ease-in"] = Properties.Resources.Curve_EaseIn;
-			_easeIcons["ease-out"] = Properties.Resources.Curve_EaseOut;
-			_easeIcons["ease-in-cubic"] = Properties.Resources.Curve_EaseInCubic;
-			_easeIcons["ease-out-cubic"] = Properties.Resources.Curve_EaseOutCubic;
-			_easeIcons["ease-in-sin"] = Properties.Resources.Curve_EaseInSin;
-			_easeIcons["ease-out-sin"] = Properties.Resources.Curve_EaseOutSin;
-			_easeIcons["ease-in-out-cubic"] = Properties.Resources.Curve_EaseInOutCubic;
-			_easeIcons["ease-out-in"] = Properties.Resources.Curve_EaseOutIn;
-			_easeIcons["ease-out-in-cubic"] = Properties.Resources.Curve_EaseOutInCubic;
-			_easeIcons["elastic"] = Properties.Resources.Curve_Elastic;
-			_easeIcons["bounce"] = Properties.Resources.Curve_Bounce;
-
-			_tweenIcons = new Dictionary<string, Image>();
-			_tweenIcons["linear"] = Properties.Resources.Tween_Linear;
-			_tweenIcons["spline"] = Properties.Resources.Tween_Spline;
-			_tweenIcons["none"] = Properties.Resources.Tween_None;
+			Sprite = data as LiveSprite;
 		}
 
-		public SpriteWidget(LiveSprite sprite, Timeline timeline)
+		static SpriteWidget()
 		{
-			_timeline = timeline;
-			Sprite = sprite;
-			Sprite.Widget = this;
-			sprite.PropertyChanged += Sprite_PropertyChanged;
-			sprite.Keyframes.CollectionChanged += Keyframes_CollectionChanged;
+			_titleFill = new SolidBrush(Color.White);
+			_subrowFill = new SolidBrush(Color.White);
+			_selectedSubrowFill = new SolidBrush(Color.White);
+			SetSpriteSkin(SkinManager.Instance.CurrentSkin);
 		}
 
-		private void Keyframes_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+		protected override void OnSetDefaultColors()
 		{
-			if (_selected)
-			{
-				if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
-				{
-					LiveKeyframe kf = e.NewItems[0] as LiveKeyframe;
-					SelectKeyframe(kf, null, false);
-					_timeline.SelectData(kf);
-				}
-			}
+			_titleFill.Color = Color.FromArgb(153, 197, 255);
+			_subrowFill.Color = Color.FromArgb(203, 206, 216);
+			_selectedSubrowFill.Color = Color.FromArgb(223, 226, 236);
 		}
 
-		public override string ToString()
+		protected override void OnUpdateSkin(Skin skin)
 		{
-			return string.IsNullOrEmpty(Sprite.Id) ? "Empty Sprite" : Sprite.Id;
+			SetSpriteSkin(skin);
 		}
 
-		public void AdvanceSubWidget(bool forward)
+		private static void SetSpriteSkin(Skin skin)
 		{
-			if (Sprite.Keyframes.Count > 0)
-			{
-				int index = 0;
-				if (_selectedFrame != null)
-				{
-					index = Sprite.Keyframes.IndexOf(_selectedFrame);
-					if (forward)
-					{
-						index++;
-						if (index >= Sprite.Keyframes.Count)
-						{
-							index = 0;
-						}
-					}
-					else
-					{
-						index--;
-						if (index < 0)
-						{
-							index = Sprite.Keyframes.Count - 1;
-						}
-					}
-				}
-				LiveKeyframe kf = Sprite.Keyframes[index];
-				SelectKeyframe(kf, null, false);
-				_timeline.CurrentTime = kf.Time + Sprite.Start;
-				SelectFrameDataWithPreview(_timeline.CurrentTime);
-			}
+			_titleFill.Color = skin.GetAppColor("WidgetHeaderRow");
+			_subrowFill.Color = skin.GetAppColor("WidgetRow");
+			_selectedSubrowFill.Color = skin.GetAppColor("WidgetRowSelected");
 		}
 
-		public void OnWidgetSelectionChanged(WidgetSelectionArgs args)
+		public SpriteWidget(LiveSprite sprite, Timeline timeline) : base(sprite, timeline)
 		{
-			_timeline = args.Timeline;
-			if (args.Modifiers.HasFlag(Keys.Control))
-			{
-				return;
-			}
-			_selected = (args.IsSelected != SelectionType.Deselect);
-			if (args.IsSelected != SelectionType.Select)
-			{
-				_hoverFrame = null;
-				ClearSelection();
-			}
-			else
-			{
-				float time = args.Timeline.CurrentTime;
-				LiveKeyframe kf = Sprite.Keyframes.Find(k => k.Time == time);
-				if (kf == null)
-				{
-					args.Timeline.SelectData(Sprite);
-					if (!Sprite.IsVisible)
-					{
-						args.Timeline.CurrentTime = Sprite.Start;
-					}
-				}
-				else
-				{
-					SelectKeyframe(kf, null, false);
-					args.Timeline.SelectData(kf);
-				}
-			}
-		}
-
-		public void OnPlaybackChanged(bool playing)
-		{
-			_playing = playing;
-		}
-
-		public object GetData()
-		{
-			return Sprite;
+			_timeline = timeline;
+			Sprite = sprite;
+			Sprite.Widget = this;
+			AllowParenting = sprite.Data is LivePose;
 		}
 
-		private void Invalidate()
+		public override string ToString()
 		{
-			Invalidated?.Invoke(this, EventArgs.Empty);
+			return string.IsNullOrEmpty(Sprite.Id) ? "Empty Sprite" : Sprite.Id;
 		}
 
-		private void Sprite_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		protected override void OnDataPropertyChanged(object sender, PropertyChangedEventArgs e)
 		{
 			if (e.PropertyName == "Keyframes")
 			{
 				_thumbnail?.Dispose();
 				_thumbnail = null;
 			}
-			Invalidate();
-		}
-
-		public bool LinkedToEnd
-		{
-			get { return Sprite.LinkedToEnd; }
 		}
 
-		public bool IsResizable
+		protected override Brush GetFillBrush(bool selected)
 		{
-			get { return true; }
+			return selected ? _selectedSubrowFill : _subrowFill;
 		}
 
-		public int GetRowCount()
+		protected override Brush GetTitleBrush()
 		{
-			return _collapsed ? 1 : Sprite.AnimatedProperties.Count + 1;
+			return _titleFill;
 		}
 
-		public float GetStart()
-		{
-			return Sprite.Start;
-		}
-		public void SetStart(float time)
+		protected override int GetExtraHeaderIconCount()
 		{
-			Sprite.Start = time;
+			return 1;
 		}
 
-		public float GetLength(float duration)
+		protected override Image GetExtraHeaderIcon(int iconIndex)
 		{
-			if (LinkedToEnd && duration != 0)
+			switch (iconIndex)
 			{
-				return duration - GetStart();
-			}
-			else
-			{
-				return Sprite.Length;
-			}
-		}
-		public void SetLength(float value)
-		{
-			Sprite.Length = value;
-		}
-
-		public Brush GetFillBrush()
-		{
-			return _selected ? _selectedSubrowFill : _subrowFill;
-		}
-
-		public void ClearSelection()
-		{
-			_selectedFrame = null;
-			_selectedProperties.Clear();
-			Invalidate();
-		}
-
-		public List<string> SelectedProperties
-		{
-			get
-			{
-				List<string> properties = new List<string>();
-				foreach (string property in _selectedProperties)
-				{
-					properties.Add(property);
-				}
-				return properties;
-			}
-		}
-
-		public LiveKeyframe SelectedFrame
-		{
-			get { return _selectedFrame; }
-		}
-
-		public void SelectKeyframe(LiveKeyframe keyframe, string property, bool addToSelection)
-		{
-			Invalidate();
-			if (_selectedFrame != keyframe || property == null)
-			{
-				_selectedProperties.Clear();
-			}
-			else if (_selectedFrame != null && _selectedProperties.Count == 0 && property != null)
-			{
-				//selecting an individual property after selecting the whole frame - ignore
-				return;
-			}
-			else
-			{
-				bool alreadySelected = _selectedProperties.Contains(property);
-				if (alreadySelected)
-				{
-					if (addToSelection)
-					{
-						_selectedProperties.Remove(property);
-						return;
-					}
-				}
-				else
-				{
-					if (!addToSelection)
-					{
-						_selectedProperties.Clear();
-					}
-				}
-			}
-			_selectedFrame = keyframe;
-			if (property != null)
-			{
-				_selectedProperties.Add(property);
-			}
-		}
-
-		public int GetHeaderIconCount(int row)
-		{
-			return row == 0 ? 2 : 3;
-		}
-
-		public void DrawHeaderIcon(Graphics g, int rowIndex, int iconIndex, int x, int y, int iconWidth, int highlightedIconIndex)
-		{
-			Image icon = null;
-			if (rowIndex == 0)
-			{
-				switch (iconIndex)
-				{
-					case 0:
-						icon = Sprite.Hidden ? Properties.Resources.EyeClosed : Properties.Resources.EyeOpen;
-						break;
-					case 1:
-						if (Sprite.Parent != null)
-						{
-							icon = Sprite.Parent.Image;
-						}
-						else
-						{
-							icon = Properties.Resources.AddLink;
-						}
-						break;
-				}
-			}
-			else
-			{
-				string property = Sprite.Properties[rowIndex - 1];
-				AnimatedProperty propData = Sprite.GetAnimationProperties(property);
-				switch (iconIndex)
-				{
-					case 0:
-						if (highlightedIconIndex == 0)
-						{
-							icon = Properties.Resources.Loop;
-						}
-						else
-						{
-							icon = (propData.Looped ? Properties.Resources.Loop : null);
-						}
-						break;
-					case 1:
-						string tween = propData.Interpolation.GetValue(_timeline.CurrentTime) ?? "none";
-						if (!_tweenIcons.TryGetValue(tween, out icon))
-						{
-							icon = _tweenIcons["none"];
-						}
-						break;
-					case 2:
-						string ease = propData.Ease.GetValue(_timeline.CurrentTime) ?? "smooth";
-						if (!_easeIcons.TryGetValue(ease, out icon))
-						{
-							icon = _easeIcons["smooth"];
-						}
-						break;
-				}
-			}
-			if (icon != null)
-			{
-				g.DrawImage(icon, x, y, iconWidth, iconWidth);
-			}
-		}
-
-		public void DrawContents(Graphics g, int rowIndex, int x, int y, float pps, int widgetWidth, int rowHeight)
-		{
-			g.DrawLine(Pens.DarkGray, x + 1, y + rowHeight + 1, x + widgetWidth - 1, y + rowHeight + 1);
-			if (rowIndex == 0)
-			{
-				g.FillRectangle(_titleFill, x, y, widgetWidth, rowHeight + (GetRowCount() == 1 ? 0 : 1));
-				//global keyframes
-				foreach (LiveKeyframe kf in Sprite.Keyframes)
-				{
-					DrawKeyframe(g, (kf == _selectedFrame && _selectedProperties.Count == 0) || (kf == _hoverFrame && _hoverRow == 0) ? _keyframeFillSelected : _headerKeyframeFill, TimeToX(Sprite.Start + kf.Time, pps), y + rowHeight / 2 - KeyframeRadius - 1, kf, null);
-				}
-			}
-			else
-			{
-				string property = Sprite.Properties[rowIndex - 1];
-
-				if (_playing)
-				{
-					float start, end;
-					float time = Sprite.GetInterpolatedTime(property, Sprite.Time, null, null, out start, out end);
-					int right = TimeToX(end + Sprite.Start, pps);
-					int left = TimeToX(start + Sprite.Start, pps);
-					int width = (right - left);
-					int interpolatedX = left + (int)(width * time);
-					g.FillRectangle(Brushes.DarkBlue, interpolatedX - 1, y, 3, rowHeight);
-				}
-
-				AnimatedProperty prop = Sprite.GetAnimationProperties(property);
-				if (prop.Looped)
-				{
-					//draw a repeat sign after the last keyframe containing this property
-					for (int i = Sprite.Keyframes.Count - 1; i >= 0; i--)
-					{
-						LiveKeyframe kf = Sprite.Keyframes[i];
-						if (kf.HasProperty(property))
-						{
-							int repeatX = TimeToX(Sprite.Start + kf.Time, pps);
-							g.FillEllipse(_repeatFill, repeatX + 6, y + rowHeight / 3 - 2, 4, 4);
-							g.FillEllipse(_repeatFill, repeatX + 6, y + 2 * rowHeight / 3 - 2, 4, 4);
-							g.FillRectangle(_repeatFill, repeatX + 11, y, 1, rowHeight + 1);
-							g.FillRectangle(_repeatFill, repeatX + 13, y, 3, rowHeight + 1);
-							break;
-						}
-					}
-				}
-
-				foreach (LiveKeyframe kf in Sprite.Keyframes)
-				{
-					if (kf.HasProperty(property))
-					{
-						DrawKeyframe(g, (kf == _selectedFrame && (_selectedProperties.Count == 0 || _selectedProperties.Contains(property))) || (kf == _hoverFrame && (_hoverRow == 0 || _hoverRow == rowIndex)) ? _keyframeFillSelected : _keyframeFill, TimeToX(Sprite.Start + kf.Time, pps), y + rowHeight / 2 - KeyframeRadius - 1, kf, property);
-					}
-				}
-			}
-		}
-
-		private void DrawKeyframe(Graphics g, Brush brush, int x, int y, LiveKeyframe kf, string property)
-		{
-			y += KeyframeRadius + 1;
-			Point[] pts;
-			if (property != null && kf.InterpolationBreaks.ContainsKey(property))
-			{
-				pts = new Point[] { new Point(x, y - KeyframeRadius), new Point(x + KeyframeRadius, y), new Point(x, y + KeyframeRadius) };
-			}
-			else
-			{
-				pts = new Point[] { new Point(x - KeyframeRadius, y), new Point(x, y - KeyframeRadius), new Point(x + KeyframeRadius, y), new Point(x, y + KeyframeRadius) };
-			}
-			g.FillPolygon(brush, pts);
-			g.DrawPolygon(_penKeyframe, pts);
-		}
-
-		private int TimeToX(float time, float pps)
-		{
-			return (int)(time * pps);
-		}
-
-		public string GetLabel(int row)
-		{
-			if (row == 0)
-			{
-				return Sprite.Id;
-			}
-			else
-			{
-				string property = Sprite.Properties[row - 1];
-				PropertyDefinition definition = Definitions.Instance.Get<PropertyDefinition>(property);
-				if (definition != null)
-				{
-					return definition.Name;
-				}
-				return "Unknown property";
+				case 0:
+					return Properties.Resources.Image;
 			}
+			return null;
 		}
 
-		public Image GetThumbnail()
+		public override Image GetThumbnail()
 		{
-			if (_thumbnail == null && Sprite.Properties.Contains("Src"))
+			if (_thumbnail == null && Data.Properties.Contains("Src"))
 			{
-				string src = Sprite.GetPropertyValue<string>("Src", 0, null);
+				string src = Data.GetPropertyValue<string>("Src", 0, 0, null);
 				if (!string.IsNullOrEmpty(src))
 				{
 					try
@@ -481,570 +121,28 @@ namespace SPNATI_Character_Editor.EpilogueEditor
 			return _thumbnail;
 		}
 
-		public string GetHeaderTooltip(WidgetActionArgs args, int iconIndex)
-		{
-			if (args.Row > 0)
-			{
-				string property = Sprite.Properties[args.Row - 1];
-				AnimatedProperty prop = Sprite.GetAnimationProperties(property);
-				switch (iconIndex)
-				{
-					case 0:
-						return "Toggle looping";
-					case 1:
-						return $"Tweening: {prop.Interpolation.GetValue(_timeline.CurrentTime) ?? "none"}";
-					case 2:
-						return $"Easing method: {prop.Ease.GetValue(_timeline.CurrentTime) ?? "smooth"}";
-				}
-			}
-			else
-			{
-				switch (iconIndex)
-				{
-					case 0:
-						return "Toggle visibility";
-					case 1:
-						return Sprite.Parent == null ? "Unlinked" : $"Linked to: {Sprite.ParentId}";
-				}
-			}
-			return null;
-		}
-
-		public void OnClickHeaderIcon(WidgetActionArgs args, int iconIndex)
-		{
-			if (args.Row == 0)
-			{
-				switch (iconIndex)
-				{
-					case 0:
-						Sprite.Hidden = !Sprite.Hidden;
-						break;
-					case 1:
-						List<LiveSprite> sprites = new List<LiveSprite>();
-						foreach (LiveSprite sprite in Sprite.Pose.Sprites)
-						{
-							if (string.IsNullOrEmpty(sprite.Id) || sprite == Sprite)
-							{
-								continue;
-							}
-							//if this is an ancestor of the sprite, disallow it to avoid infinite chains
-							LiveSprite parent = sprite.Parent;
-							bool isAncestor = false;
-							while (parent != null)
-							{
-								if (parent == Sprite)
-								{
-									isAncestor = true;
-									break;
-								}
-								parent = parent.Parent;
-							}
-							if (!isAncestor)
-							{
-								sprites.Add(sprite);
-							}
-						}
-						sprites.Sort();
-						ContextMenuItem[] items = new ContextMenuItem[sprites.Count + 1];
-						items[0] = new ContextMenuItem("Unlinked", null, SelectParent, null, Sprite.Parent == null);
-						for (int i = 0; i < sprites.Count; i++)
-						{
-							LiveSprite sprite = sprites[i];
-							items[i + 1] = new ContextMenuItem(sprite.Id, sprite.Image, SelectParent, sprite.Id, Sprite.Parent == sprite);
-						}
-						args.Timeline.ShowContextMenu(items);
-						break;
-				}
-			}
-			else
-			{
-				string property = Sprite.Properties[args.Row - 1];
-				AnimatedProperty prop = Sprite.GetAnimationProperties(property);
-				switch (iconIndex)
-				{
-					case 0:
-						prop.Looped = !prop.Looped;
-						break;
-					case 1:
-						string tween = prop.Interpolation.GetValue(args.Time);
-						args.Timeline.ShowContextMenu(
-							new ContextMenuItem("Linear", Properties.Resources.Tween_Linear, SelectTween, new Tuple<string, string>(property, "linear"), tween == "linear"),
-							new ContextMenuItem("Spline", Properties.Resources.Tween_Spline, SelectTween, new Tuple<string, string>(property, "spline"), tween == "spline"),
-							new ContextMenuItem("None", Properties.Resources.Tween_None, SelectTween, new Tuple<string, string>(property, "none"), tween == "none" || string.IsNullOrEmpty(tween))
-							);
-						break;
-					case 2:
-						string ease = prop.Ease.GetValue(args.Time);
-						args.Timeline.ShowContextMenu(
-							new ContextMenuItem("Linear", Properties.Resources.Curve_Linear, SelectEase, new Tuple<string, string>(property, "linear"), ease == "linear"),
-							new ContextMenuItem("Smooth", Properties.Resources.Curve_Smooth, SelectEase, new Tuple<string, string>(property, "smooth"), ease == "smooth" || string.IsNullOrEmpty(ease)),
-							new ContextMenuItem("Ease-In-Out Cubic", Properties.Resources.Curve_EaseInOutCubic, SelectEase, new Tuple<string, string>(property, "ease-in-out-cubic"), ease == "ease-in-out-cubic"),
-							new ContextMenuItem("Ease-Out-In", Properties.Resources.Curve_EaseOutIn, SelectEase, new Tuple<string, string>(property, "ease-out-in"), ease == "ease-out-in"),
-							new ContextMenuItem("Ease-Out-In Cubic", Properties.Resources.Curve_EaseOutInCubic, SelectEase, new Tuple<string, string>(property, "ease-out-in-cubic"), ease == "ease-out-in-cubic"),
-							new ContextMenuItem("Ease-In", Properties.Resources.Curve_EaseIn, SelectEase, new Tuple<string, string>(property, "ease-in"), ease == "ease-in"),
-							new ContextMenuItem("Ease-In Sine", Properties.Resources.Curve_EaseInSin, SelectEase, new Tuple<string, string>(property, "ease-in-sin"), ease == "ease-in-sin"),
-							new ContextMenuItem("Ease-In Cubic", Properties.Resources.Curve_EaseInCubic, SelectEase, new Tuple<string, string>(property, "ease-in-cubic"), ease == "ease-in-cubic"),
-							new ContextMenuItem("Ease-Out", Properties.Resources.Curve_EaseOut, SelectEase, new Tuple<string, string>(property, "ease-out"), ease == "ease-out"),
-							new ContextMenuItem("Ease-Out Sine", Properties.Resources.Curve_EaseOutSin, SelectEase, new Tuple<string, string>(property, "ease-out-sin"), ease == "ease-out-sin"),
-							new ContextMenuItem("Ease-Out Cubic", Properties.Resources.Curve_EaseOutCubic, SelectEase, new Tuple<string, string>(property, "ease-out-cubic"), ease == "ease-out-cubic"),
-							new ContextMenuItem("Bounce", Properties.Resources.Curve_Bounce, SelectEase, new Tuple<string, string>(property, "bounce"), ease == "bounce"),
-							new ContextMenuItem("Elastic", Properties.Resources.Curve_Elastic, SelectEase, new Tuple<string, string>(property, "elastic"), ease == "elastic")
-							);
-						break;
-				}
-			}
-		}
-
-		private void SelectTween(object sender, EventArgs e)
-		{
-			//TODO: Make this an ICommand
-			ToolStripMenuItem item = sender as ToolStripMenuItem;
-			Tuple<string, string> tag = item.Tag as Tuple<string, string>;
-			string property = tag.Item1;
-			string tween = tag.Item2;
-			AnimatedProperty prop = Sprite.GetAnimationProperties(property);
-
-			//get the time of the most recent split
-			for (int i = Sprite.Keyframes.Count - 1; i >= 0; i--)
-			{
-				LiveKeyframe kf = Sprite.Keyframes[i];
-				if (kf.Time <= _timeline.CurrentTime && kf.InterpolationBreaks.ContainsKey(property))
-				{
-					prop.Interpolation.SetValue(kf.Time, tween);
-					return;
-				}
-			}
-			prop.Interpolation.SetValue(0, tween);
-		}
-
-		private void SelectEase(object sender, EventArgs e)
-		{
-			//TODO: Make this an ICommand
-			ToolStripMenuItem item = sender as ToolStripMenuItem;
-			Tuple<string, string> tag = item.Tag as Tuple<string, string>;
-			string property = tag.Item1;
-			string ease = tag.Item2;
-			AnimatedProperty prop = Sprite.GetAnimationProperties(property);
-
-			//get the time of the most recent split
-			for (int i = Sprite.Keyframes.Count - 1; i >= 0; i--)
-			{
-				LiveKeyframe kf = Sprite.Keyframes[i];
-				if (kf.Time <= _timeline.CurrentTime && kf.InterpolationBreaks.ContainsKey(property))
-				{
-					prop.Ease.SetValue(kf.Time, ease);
-					return;
-				}
-			}
-			prop.Ease.SetValue(0, ease);
-		}
-
-		private void SelectParent(object sender, EventArgs e)
-		{
-			//TODO: Make this an ICommand
-			ToolStripMenuItem item = sender as ToolStripMenuItem;
-			string id = item.Tag?.ToString();
-			Sprite.ParentId = id;
-		}
-
-		public void OnClickHeader(WidgetActionArgs args)
-		{
-			int row = args.Row;
-			if (row == 0)
-			{
-				ClearSelection();
-				args.Timeline.SelectData(Sprite);
-			}
-			else
-			{
-				bool add = args.Modifiers.HasFlag(Keys.Control);
-				if (!add)
-				{
-					ClearSelection();
-				}
-				_selectedFrame = null;
-				string property = Sprite.Properties[row - 1];
-				_selectedProperties.Add(property);
-				args.Timeline.SelectData(Sprite.GetAnimationProperties(property));
-				Invalidate();
-			}
-		}
-
-		public ITimelineAction GetAction(int x, int width, float start, int row, int timelineWidth, float pps)
-		{
-			_hoverFrame = null;
-
-			//see if a keyframe is selected
-			for (int i = 0; i < Sprite.Keyframes.Count; i++)
-			{
-				LiveKeyframe kf = Sprite.Keyframes[i];
-				int kfX = TimeToX(kf.Time, pps);
-				if (Math.Abs(x - kfX) <= 5)
-				{
-					_hoverFrame = kf;
-					_hoverRow = row;
-					string property = row > 0 ? Sprite.Properties[row - 1] : null;
-					if (string.IsNullOrEmpty(property) || kf.HasProperty(property))
-					{
-						return new SelectKeyframeTimelineAction(this, kf, property);
-					}
-				}
-			}
-
-			if (Math.Abs(width - x) <= 5 && !Sprite.LinkedToEnd && Sprite.Keyframes.Count <= 1)
-			{
-				return new WidgetEndTimelineAction();
-			}
-			if (x >= 5 && x <= width - 5)
-			{
-				return new MoveWidgetTimelineAction();
-			}
-			return null;
-		}
-
-		public void OnStartMove(WidgetActionArgs args)
-		{
-			ClearSelection();
-			args.Timeline.SelectData(GetData());
-		}
-
-		public void OnMouseOut()
-		{
-			_hoverFrame = null;
-			Invalidate();
-		}
-
-		public void OnTimeChanged(WidgetOperationArgs args)
+		protected override string GetExtraHeaderTooltip(WidgetActionArgs args, int iconIndex)
 		{
-			float time = args.Time;
-
-			if (_selected)
+			switch (iconIndex)
 			{
-				if (time < Sprite.Start)
-				{
-					args.Timeline.SelectData(Sprite);
-				}
-				else
-				{
-					SelectFrameDataWithPreview(time);
-				}
+				case 0:
+					return "Change source at current frame";
 			}
+			return "";
 		}
 
-		private void SelectFrameDataWithPreview(float time)
+		protected override void OnClickExtraHeaderIcon(WidgetActionArgs args, int iconIndex)
 		{
-			LiveKeyframe previewFrame = Sprite.GetInterpolatedFrame(time - Sprite.Start);
-
-			//show whatever keyframe is under the current time, or an interpolated placeholder if there is none
-			LiveKeyframe frame = Sprite.Keyframes.Find(kf => kf.Time == time);
-			if (frame == null)
+			switch (iconIndex)
 			{
-				frame = new LiveKeyframe(time - Sprite.Start);
-				frame.Sprite = Sprite;
-				frame.PropertyChanged += NewFrame_PropertyChanged;
-			}
-			_timeline.SelectData(frame, previewFrame);
-		}
-
-		private void NewFrame_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
-		{
-			//the frame has a real property now, so add it to the sprite
-			LiveKeyframe frame = sender as LiveKeyframe;
-			frame.PropertyChanged -= NewFrame_PropertyChanged;
-			Sprite.AddKeyframe(frame);
-			_timeline.SelectData(frame);
-
-		}
-
-		public bool IsCollapsible
-		{
-			get { return Sprite.AnimatedProperties.Count > 0; }
-
-		}
-
-		public bool IsCollapsed
-		{
-			get { return _collapsed; }
-			set { _collapsed = value; }
-		}
-
-		public bool IsRowHighlighted(int row)
-		{
-			string property = row > 0 ? Sprite.Properties[row - 1] : null;
-			return row == 0 ? _selectedProperties.Count == 0 : _selectedProperties.Contains(property);
-		}
-
-		public void UpdateSelection(WidgetSelectionArgs args)
-		{
-			if (_selectedFrame != null)
-			{
-				//keyframe selected
-				if (!Sprite.Keyframes.Contains(_selectedFrame))
-				{
-					ClearSelection();
-				}
-				else if (_selectedProperties.Count > 0)
-				{
-					List<string> rows = new List<string>();
-					foreach (string property in _selectedProperties)
-					{
-						rows.Add(property);
-					}
-					foreach (string row in rows)
-					{
-						if (!_selectedFrame.HasProperty(row))
-						{
-							_selectedProperties.Remove(row);
-						}
-					}
-					if (_selectedProperties.Count == 0)
-					{
-						_selectedFrame = null;
-					}
-				}
-				if (_selectedFrame != null)
-				{
-					args.AllowCut = true;
-					args.AllowCopy = true;
-					args.AllowDelete = _selectedFrame.Time > 0 || _selectedProperties.Count > 0;
-					args.AllowDuplicate = true;
-				}
-			}
-			if (_selectedFrame == null)
-			{
-				if (_selectedProperties.Count == 0)
-				{
-					//widget selected
-					args.AllowCut = true;
-					args.AllowCopy = true;
-					args.AllowDelete = true;
-					args.AllowDuplicate = true;
-				}
-				else
-				{
-					//one or more rows are selected
-					args.AllowCut = true;
-					args.AllowCopy = true;
-					args.AllowDelete = true;
-					args.AllowDuplicate = false;
-				}
-			}
-
-			args.AllowPaste = false;
-			object clipboardData = Clipboards.Get<SpriteWidget, object>();
-			if (clipboardData is AnimatedPropertyClipboardData)
-			{
-				args.AllowPaste = true;
-			}
-			else if (clipboardData is LiveSprite)
-			{
-				args.AllowPaste = true;
-			}
-			else if (clipboardData is LiveKeyframe)
-			{
-				args.AllowPaste = args.Timeline.CurrentTime >= Sprite.Start;
-			}
-		}
-
-		public bool OnCopy(WidgetOperationArgs args)
-		{
-			if (_selectedFrame != null)
-			{
-				//copy a frame
-				LiveKeyframe kf = Sprite.CopyKeyframe(_selectedFrame, _selectedProperties);
-				Clipboards.Set<SpriteWidget>(kf);
-				return true;
-			}
-			else if (_selectedProperties.Count > 0)
-			{
-				//copy a property across the animation
-				List<string> properties = new List<string>();
-				properties.AddRange(_selectedProperties);
-				AnimatedPropertyClipboardData data = new AnimatedPropertyClipboardData(Sprite, properties);
-				Clipboards.Set<SpriteWidget>(data);
-				return true;
-			}
-			else
-			{
-				//copy the whole sprite
-				LiveSprite sprite = Sprite.Copy();
-				Clipboards.Set<SpriteWidget>(sprite);
-				return true;
-			}
-		}
-
-		public bool OnDelete(WidgetOperationArgs args)
-		{
-			if (_selectedFrame != null)
-			{
-				if (_selectedProperties.Count == 0)
-				{
-					DeleteKeyframeCommand command = new DeleteKeyframeCommand(Sprite, _selectedFrame);
-					args.History.Commit(command);
-				}
-				else
-				{
-					args.History.StartBulkRecord();
-					foreach (string property in _selectedProperties)
-					{
-						DeletePropertyCommand command = new DeletePropertyCommand(Sprite, _selectedFrame, property);
-						command.Do();
-						args.History.Record(command);
-					}
-					args.History.EndBulkRecord();
-				}
-			}
-			else if (_selectedProperties.Count > 0)
-			{
-				//delete properties
-				args.History.StartBulkRecord();
-				foreach (string property in _selectedProperties)
-				{
-					DeleteAnimatedPropertyCommand command = new DeleteAnimatedPropertyCommand(Sprite, property);
-					command.Do();
-					args.History.Record(command);
-				}
-				args.History.EndBulkRecord();
-			}
-			else
-			{
-				//delete whole widget
-				if (args.IsSilent || MessageBox.Show($"Are you sure you want to completely remove {ToString()}?", "Remove Sprite", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
-				{
-					DeleteWidgetCommand command = new DeleteWidgetCommand(Sprite.Pose, this);
-					args.History.Commit(command);
-					args.Timeline.SelectData(null);
-				}
-			}
-			return true;
-		}
-
-		public bool OnPaste(WidgetOperationArgs args)
-		{
-			object clipboardData = Clipboards.Get<SpriteWidget, object>();
-			if (clipboardData is AnimatedPropertyClipboardData)
-			{
-				AnimatedPropertyClipboardData data = clipboardData as AnimatedPropertyClipboardData;
-				if (data != null)
-				{
-					data.Apply(Sprite);
-					args.History.Record(data);
-				}
-			}
-			else if (clipboardData is LiveKeyframe)
-			{
-				//Pastes a copied frame into the current position, overwriting any properties in frame already there
-				LiveKeyframe copiedFrame = clipboardData as LiveKeyframe;
-				if (copiedFrame != null)
-				{
-					float time = args.Time;
-
-					//if a keyframe is nearby, paste into it
-					int x = args.Timeline.TimeToX(time);
-					float minTime = args.Timeline.XToTime(x - KeyframeRadius);
-					float maxTime = args.Timeline.XToTime(x + KeyframeRadius);
-					LiveKeyframe frame = Sprite.Keyframes.Find(k => minTime <= k.Time && maxTime >= k.Time);
+				case 0:
+					LiveKeyframe frame = SelectFrameDataWithPreview(0);
 					if (frame != null)
 					{
-						time = frame.Time;
-					}
-
-					PasteKeyframeCommand command = new PasteKeyframeCommand(Sprite, copiedFrame, time);
-					args.History.Commit(command);
-					_selectedFrame = command.NewKeyframe;
-					_selectedProperties.Clear();
-					args.Timeline.SelectData(_selectedFrame);
-				}
-			}
-			else if (clipboardData is LiveSprite)
-			{
-				return Sprite.Pose.Paste(args, Sprite.Pose.Sprites.IndexOf(Sprite) + 1);
-			}
-			return true;
-		}
-
-		public bool OnDuplicate(WidgetOperationArgs args)
-		{
-			if (_selectedFrame == null)
-			{
-				//duplicating whole sprite
-				LiveSprite sprite = Sprite.Copy();
-				object oldClipboard = Clipboards.Get<SpriteWidget, object>();
-				Clipboards.Set<SpriteWidget>(sprite);
-				Sprite.Pose.Paste(args, Sprite.Pose.Sprites.IndexOf(Sprite) + 1);
-				Clipboards.Set<SpriteWidget>(oldClipboard);
-				return true;
-			}
-			else
-			{
-				//duplicating keyframe
-				float time = args.Time;
-
-				//if a keyframe is nearby, paste into it
-				int x = args.Timeline.TimeToX(time);
-				float minTime = args.Timeline.XToTime(x - KeyframeRadius);
-				float maxTime = args.Timeline.XToTime(x + KeyframeRadius);
-				LiveKeyframe frame = Sprite.Keyframes.Find(k => minTime <= k.Time && maxTime >= k.Time);
-				if (frame != null)
-				{
-					time = frame.Time;
-				}
-				if (Math.Abs(_selectedFrame.Time + Sprite.Start - time) < 0.001f)
-				{
-					return false; //can't duplicate into itself
-				}
-
-				PasteKeyframeCommand command = new PasteKeyframeCommand(Sprite, _selectedFrame, time);
-				args.History.Commit(command);
-				_selectedFrame = command.NewKeyframe;
-				_selectedProperties.Clear();
-				args.Timeline.SelectData(_selectedFrame);
-				return true;
-			}
-		}
-
-		public void OnOpeningContextMenu(ContextMenuArgs args)
-		{
-			if (_selectedFrame != null)
-			{
-				bool split = false;
-				if (_selectedProperties.Count == 0)
-				{
-					foreach (string property in LiveKeyframe.TrackedProperties)
-					{
-						if (_selectedFrame.HasProperty(property))
-						{
-							if (_selectedFrame.InterpolationBreaks.ContainsKey(property))
-							{
-								split = true;
-								break;
-							}
-						}
+						_timeline.RequestUI(frame);
 					}
-				}
-				else
-				{
-					foreach (string property in _selectedProperties)
-					{
-						if (_selectedFrame.InterpolationBreaks.ContainsKey(property))
-						{
-							split = true;
-							break;
-						}
-					}
-				}
-				args.ItemsToAdd.Add(new ContextMenuItem("Split animation", Properties.Resources.SplitKeyframe, ToggleSplit, null, split));
-			}
-		}
-
-		private void ToggleSplit(object sender, EventArgs e)
-		{
-			if (_selectedFrame == null)
-			{
-				return;
+					break;
 			}
-			ToggleAnimationBreakCommand command = new ToggleAnimationBreakCommand(Sprite, _selectedFrame, _selectedProperties);
-			_timeline.CommandHistory.Commit(command);
 		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/TextWidget.cs b/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/TextWidget.cs
new file mode 100644
index 0000000000000000000000000000000000000000..50b33944315128413b050de07739069826861351
--- /dev/null
+++ b/editor source/SPNATI Character Editor/EpilogueEditing/Widgets/TextWidget.cs	
@@ -0,0 +1,61 @@
+using System;
+using System.Drawing;
+using Desktop.Skinning;
+
+namespace SPNATI_Character_Editor.EpilogueEditor
+{
+	public class TextWidget : KeyframedWidget
+	{
+		private SolidBrush _fillBrush;
+		private SolidBrush _selectedBrush;
+
+		public TextWidget(LiveAnimatedObject data, Timeline timeline) : base(data, timeline)
+		{
+			data.PropertyChanged += Bubble_PropertyChanged;
+			_fillBrush = new SolidBrush(Color.LightGray);
+			_selectedBrush = new SolidBrush(Color.White);
+			ShowPropertyRows = false;
+		}
+
+		private void Bubble_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+		{
+			if (e.PropertyName == "Text")
+			{
+				Invalidate();
+			}
+		}
+
+		protected override Brush GetFillBrush(bool selected)
+		{
+			return selected ? _selectedBrush : _fillBrush;
+		}
+
+		public override Image GetThumbnail()
+		{
+			return Properties.Resources.SpeechBubble;
+		}
+
+		protected override Brush GetTitleBrush()
+		{
+			return Brushes.HotPink;
+		}
+
+		public override string GetLabel(int row)
+		{
+			if (row == 0)
+			{
+				LiveBubbleKeyframe kf = Data.Keyframes[0] as LiveBubbleKeyframe;
+				return kf.Text ?? Data.Id;
+			}
+			else
+			{
+				return base.GetLabel(row);
+			}
+		}
+
+		public override string ToString()
+		{
+			return $"Text: {Data.ToString()}";
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/ExtensionMethods.cs b/editor source/SPNATI Character Editor/ExtensionMethods.cs
index c979aeedb7054c0ffba3b408f7b50c9f929cbcd8..a8534f6b8058d411dc7c314df298c3816eb12b2b 100644
--- a/editor source/SPNATI Character Editor/ExtensionMethods.cs	
+++ b/editor source/SPNATI Character Editor/ExtensionMethods.cs	
@@ -38,5 +38,22 @@ namespace SPNATI_Character_Editor
 			int index = _random.Next(list.Count);
 			return list[index];
 		}
+
+		public static void AddRange<T>(this ObservableCollection<T> list, IEnumerable<T> items)
+		{
+			foreach (T item in items)
+			{
+				list.Add(item);
+			}
+		}
+
+		public static void Sort<T>(this ObservableCollection<T> list)
+		{
+			List<T> temp = new List<T>();
+			temp.AddRange(list);
+			temp.Sort();
+			list.Clear();
+			list.AddRange(temp);
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/FileNameSelect.Designer.cs b/editor source/SPNATI Character Editor/FileNameSelect.Designer.cs
deleted file mode 100644
index a49f9c8f09975718fd7d60b699c77dd8321f4929..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/FileNameSelect.Designer.cs	
+++ /dev/null
@@ -1,105 +0,0 @@
-namespace SPNATI_Character_Editor
-{
-	partial class FileNameSelect
-	{
-		/// <summary>
-		/// Required designer variable.
-		/// </summary>
-		private System.ComponentModel.IContainer components = null;
-
-		/// <summary>
-		/// Clean up any resources being used.
-		/// </summary>
-		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
-		protected override void Dispose(bool disposing)
-		{
-			if (disposing && (components != null))
-			{
-				components.Dispose();
-			}
-			base.Dispose(disposing);
-		}
-
-		#region Windows Form Designer generated code
-
-		/// <summary>
-		/// Required method for Designer support - do not modify
-		/// the contents of this method with the code editor.
-		/// </summary>
-		private void InitializeComponent()
-		{
-			this.txtFile = new System.Windows.Forms.TextBox();
-			this.label1 = new System.Windows.Forms.Label();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.SuspendLayout();
-			// 
-			// txtFile
-			// 
-			this.txtFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.txtFile.Location = new System.Drawing.Point(88, 12);
-			this.txtFile.Name = "txtFile";
-			this.txtFile.Size = new System.Drawing.Size(161, 20);
-			this.txtFile.TabIndex = 0;
-			// 
-			// label1
-			// 
-			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(12, 15);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(70, 13);
-			this.label1.TabIndex = 1;
-			this.label1.Text = "Folder Name:";
-			// 
-			// cmdOK
-			// 
-			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(93, 40);
-			this.cmdOK.Name = "cmdOK";
-			this.cmdOK.Size = new System.Drawing.Size(75, 23);
-			this.cmdOK.TabIndex = 2;
-			this.cmdOK.Text = "OK";
-			this.cmdOK.UseVisualStyleBackColor = true;
-			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
-			// 
-			// cmdCancel
-			// 
-			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdCancel.Location = new System.Drawing.Point(174, 40);
-			this.cmdCancel.Name = "cmdCancel";
-			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
-			this.cmdCancel.TabIndex = 3;
-			this.cmdCancel.Text = "Cancel";
-			this.cmdCancel.UseVisualStyleBackColor = true;
-			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
-			// 
-			// FileNameSelect
-			// 
-			this.AcceptButton = this.cmdOK;
-			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(261, 75);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
-			this.Controls.Add(this.label1);
-			this.Controls.Add(this.txtFile);
-			this.Name = "FileNameSelect";
-			this.ShowIcon = false;
-			this.ShowInTaskbar = false;
-			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
-			this.Text = "File Select";
-			this.ResumeLayout(false);
-			this.PerformLayout();
-
-		}
-
-		#endregion
-
-		private System.Windows.Forms.TextBox txtFile;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
-	}
-}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/FileNameSelect.cs b/editor source/SPNATI Character Editor/FileNameSelect.cs
deleted file mode 100644
index 3c1e2a61408afab6e2024df8ef6f35fb34d3a246..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/FileNameSelect.cs	
+++ /dev/null
@@ -1,52 +0,0 @@
-using System.IO;
-using System.Windows.Forms;
-
-namespace SPNATI_Character_Editor
-{
-	/// <summary>
-	/// Selection form for choosing a destination to save a character
-	/// </summary>
-	public partial class FileNameSelect : Form
-	{
-		private string _folderName;
-		public string FolderName
-		{
-			get { return _folderName; }
-			set
-			{
-				_folderName = value;
-				txtFile.Text = value;
-			}
-		}
-
-		public FileNameSelect()
-		{
-			InitializeComponent();
-		}
-
-		private void cmdOK_Click(object sender, System.EventArgs e)
-		{
-			string name = txtFile.Text;
-			string folder = Config.GetRootDirectory(name);
-
-			if (name != FolderName && Directory.Exists(folder))
-			{
-				string text = string.Format("{0} already exists. Do you really want to overwrite this character?", name);
-				if (MessageBox.Show(text, "Save As", MessageBoxButtons.YesNo) != DialogResult.Yes)
-				{
-					return;
-				}
-			}
-			FolderName = name;
-
-			DialogResult = DialogResult.OK;
-			this.Close();
-		}
-
-		private void cmdCancel_Click(object sender, System.EventArgs e)
-		{
-			DialogResult = DialogResult.Cancel;
-			this.Close();
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Forms/CanvasHelp.Designer.cs b/editor source/SPNATI Character Editor/Forms/CanvasHelp.Designer.cs
index d8ce8aaf0632fa5144797d1c4a9d5f656ee7ff45..be505e64f439e0cb1caee0fd2985b020533ab115 100644
--- a/editor source/SPNATI Character Editor/Forms/CanvasHelp.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/CanvasHelp.Designer.cs	
@@ -28,48 +28,50 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.groupBox1 = new System.Windows.Forms.GroupBox();
-			this.label4 = new System.Windows.Forms.Label();
-			this.label3 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
-			this.label1 = new System.Windows.Forms.Label();
-			this.groupBox2 = new System.Windows.Forms.GroupBox();
-			this.label13 = new System.Windows.Forms.Label();
-			this.label14 = new System.Windows.Forms.Label();
-			this.label12 = new System.Windows.Forms.Label();
-			this.label11 = new System.Windows.Forms.Label();
-			this.label9 = new System.Windows.Forms.Label();
-			this.label10 = new System.Windows.Forms.Label();
-			this.label5 = new System.Windows.Forms.Label();
-			this.label6 = new System.Windows.Forms.Label();
-			this.label7 = new System.Windows.Forms.Label();
-			this.label8 = new System.Windows.Forms.Label();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.label15 = new System.Windows.Forms.Label();
-			this.label16 = new System.Windows.Forms.Label();
-			this.groupBox3 = new System.Windows.Forms.GroupBox();
-			this.label17 = new System.Windows.Forms.Label();
-			this.label18 = new System.Windows.Forms.Label();
-			this.label19 = new System.Windows.Forms.Label();
-			this.label20 = new System.Windows.Forms.Label();
-			this.label21 = new System.Windows.Forms.Label();
-			this.label22 = new System.Windows.Forms.Label();
-			this.label23 = new System.Windows.Forms.Label();
-			this.label24 = new System.Windows.Forms.Label();
+			this.groupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.groupBox2 = new Desktop.Skinning.SkinnedGroupBox();
+			this.label13 = new Desktop.Skinning.SkinnedLabel();
+			this.label14 = new Desktop.Skinning.SkinnedLabel();
+			this.label12 = new Desktop.Skinning.SkinnedLabel();
+			this.label11 = new Desktop.Skinning.SkinnedLabel();
+			this.label9 = new Desktop.Skinning.SkinnedLabel();
+			this.label10 = new Desktop.Skinning.SkinnedLabel();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
+			this.label6 = new Desktop.Skinning.SkinnedLabel();
+			this.label7 = new Desktop.Skinning.SkinnedLabel();
+			this.label8 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.label15 = new Desktop.Skinning.SkinnedLabel();
+			this.label16 = new Desktop.Skinning.SkinnedLabel();
+			this.groupBox3 = new Desktop.Skinning.SkinnedGroupBox();
+			this.label23 = new Desktop.Skinning.SkinnedLabel();
+			this.label24 = new Desktop.Skinning.SkinnedLabel();
+			this.label21 = new Desktop.Skinning.SkinnedLabel();
+			this.label22 = new Desktop.Skinning.SkinnedLabel();
+			this.label19 = new Desktop.Skinning.SkinnedLabel();
+			this.label20 = new Desktop.Skinning.SkinnedLabel();
+			this.label17 = new Desktop.Skinning.SkinnedLabel();
+			this.label18 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
 			this.groupBox1.SuspendLayout();
 			this.groupBox2.SuspendLayout();
 			this.groupBox3.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// groupBox1
 			// 
-			this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-			| System.Windows.Forms.AnchorStyles.Right)));
+			this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
 			this.groupBox1.Controls.Add(this.label4);
 			this.groupBox1.Controls.Add(this.label3);
 			this.groupBox1.Controls.Add(this.label2);
 			this.groupBox1.Controls.Add(this.label1);
-			this.groupBox1.Location = new System.Drawing.Point(12, 12);
+			this.groupBox1.Location = new System.Drawing.Point(12, 34);
 			this.groupBox1.Name = "groupBox1";
 			this.groupBox1.Size = new System.Drawing.Size(288, 52);
 			this.groupBox1.TabIndex = 0;
@@ -79,6 +81,7 @@
 			// label4
 			// 
 			this.label4.AutoSize = true;
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label4.Location = new System.Drawing.Point(85, 33);
 			this.label4.Name = "label4";
 			this.label4.Size = new System.Drawing.Size(96, 13);
@@ -89,6 +92,7 @@
 			// 
 			this.label3.AutoSize = true;
 			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
 			this.label3.Location = new System.Drawing.Point(7, 33);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(42, 13);
@@ -98,6 +102,7 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label2.Location = new System.Drawing.Point(85, 20);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(90, 13);
@@ -108,6 +113,7 @@
 			// 
 			this.label1.AutoSize = true;
 			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
 			this.label1.Location = new System.Drawing.Point(7, 20);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(33, 13);
@@ -116,8 +122,8 @@
 			// 
 			// groupBox2
 			// 
-			this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-			| System.Windows.Forms.AnchorStyles.Right)));
+			this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
 			this.groupBox2.Controls.Add(this.label13);
 			this.groupBox2.Controls.Add(this.label14);
 			this.groupBox2.Controls.Add(this.label12);
@@ -128,7 +134,7 @@
 			this.groupBox2.Controls.Add(this.label6);
 			this.groupBox2.Controls.Add(this.label7);
 			this.groupBox2.Controls.Add(this.label8);
-			this.groupBox2.Location = new System.Drawing.Point(12, 70);
+			this.groupBox2.Location = new System.Drawing.Point(12, 92);
 			this.groupBox2.Name = "groupBox2";
 			this.groupBox2.Size = new System.Drawing.Size(288, 94);
 			this.groupBox2.TabIndex = 1;
@@ -138,6 +144,7 @@
 			// label13
 			// 
 			this.label13.AutoSize = true;
+			this.label13.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label13.Location = new System.Drawing.Point(85, 72);
 			this.label13.Name = "label13";
 			this.label13.Size = new System.Drawing.Size(171, 13);
@@ -148,6 +155,7 @@
 			// 
 			this.label14.AutoSize = true;
 			this.label14.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label14.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
 			this.label14.Location = new System.Drawing.Point(7, 72);
 			this.label14.Name = "label14";
 			this.label14.Size = new System.Drawing.Size(42, 13);
@@ -157,6 +165,7 @@
 			// label12
 			// 
 			this.label12.AutoSize = true;
+			this.label12.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label12.Location = new System.Drawing.Point(85, 59);
 			this.label12.Name = "label12";
 			this.label12.Size = new System.Drawing.Size(163, 13);
@@ -167,6 +176,7 @@
 			// 
 			this.label11.AutoSize = true;
 			this.label11.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label11.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
 			this.label11.Location = new System.Drawing.Point(7, 59);
 			this.label11.Name = "label11";
 			this.label11.Size = new System.Drawing.Size(75, 13);
@@ -176,6 +186,7 @@
 			// label9
 			// 
 			this.label9.AutoSize = true;
+			this.label9.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label9.Location = new System.Drawing.Point(85, 46);
 			this.label9.Name = "label9";
 			this.label9.Size = new System.Drawing.Size(185, 13);
@@ -186,6 +197,7 @@
 			// 
 			this.label10.AutoSize = true;
 			this.label10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label10.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
 			this.label10.Location = new System.Drawing.Point(7, 46);
 			this.label10.Name = "label10";
 			this.label10.Size = new System.Drawing.Size(49, 13);
@@ -195,6 +207,7 @@
 			// label5
 			// 
 			this.label5.AutoSize = true;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label5.Location = new System.Drawing.Point(85, 33);
 			this.label5.Name = "label5";
 			this.label5.Size = new System.Drawing.Size(187, 13);
@@ -205,6 +218,7 @@
 			// 
 			this.label6.AutoSize = true;
 			this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label6.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
 			this.label6.Location = new System.Drawing.Point(7, 33);
 			this.label6.Name = "label6";
 			this.label6.Size = new System.Drawing.Size(43, 13);
@@ -214,6 +228,7 @@
 			// label7
 			// 
 			this.label7.AutoSize = true;
+			this.label7.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label7.Location = new System.Drawing.Point(85, 20);
 			this.label7.Name = "label7";
 			this.label7.Size = new System.Drawing.Size(145, 13);
@@ -224,6 +239,7 @@
 			// 
 			this.label8.AutoSize = true;
 			this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label8.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
 			this.label8.Location = new System.Drawing.Point(7, 20);
 			this.label8.Name = "label8";
 			this.label8.Size = new System.Drawing.Size(42, 13);
@@ -232,8 +248,9 @@
 			// 
 			// cmdOK
 			// 
-			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(225, 275);
+			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdOK.Flat = true;
+			this.cmdOK.Location = new System.Drawing.Point(234, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 2;
@@ -245,6 +262,7 @@
 			// 
 			this.label15.AutoSize = true;
 			this.label15.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label15.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
 			this.label15.Location = new System.Drawing.Point(6, 20);
 			this.label15.Name = "label15";
 			this.label15.Size = new System.Drawing.Size(48, 13);
@@ -254,6 +272,7 @@
 			// label16
 			// 
 			this.label16.AutoSize = true;
+			this.label16.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label16.Location = new System.Drawing.Point(85, 20);
 			this.label16.Name = "label16";
 			this.label16.Size = new System.Drawing.Size(58, 13);
@@ -262,8 +281,8 @@
 			// 
 			// groupBox3
 			// 
-			this.groupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-			| System.Windows.Forms.AnchorStyles.Right)));
+			this.groupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
 			this.groupBox3.Controls.Add(this.label23);
 			this.groupBox3.Controls.Add(this.label24);
 			this.groupBox3.Controls.Add(this.label21);
@@ -274,35 +293,59 @@
 			this.groupBox3.Controls.Add(this.label17);
 			this.groupBox3.Controls.Add(this.label15);
 			this.groupBox3.Controls.Add(this.label18);
-			this.groupBox3.Location = new System.Drawing.Point(12, 170);
+			this.groupBox3.Location = new System.Drawing.Point(12, 192);
 			this.groupBox3.Name = "groupBox3";
 			this.groupBox3.Size = new System.Drawing.Size(288, 99);
 			this.groupBox3.TabIndex = 4;
 			this.groupBox3.TabStop = false;
 			this.groupBox3.Text = "Edit";
 			// 
-			// label17
+			// label23
 			// 
-			this.label17.AutoSize = true;
-			this.label17.Location = new System.Drawing.Point(85, 33);
-			this.label17.Name = "label17";
-			this.label17.Size = new System.Drawing.Size(41, 13);
-			this.label17.TabIndex = 3;
-			this.label17.Text = "Ctrl + X";
+			this.label23.AutoSize = true;
+			this.label23.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label23.Location = new System.Drawing.Point(85, 72);
+			this.label23.Name = "label23";
+			this.label23.Size = new System.Drawing.Size(42, 13);
+			this.label23.TabIndex = 17;
+			this.label23.Text = "Ctrl + D";
 			// 
-			// label18
+			// label24
 			// 
-			this.label18.AutoSize = true;
-			this.label18.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.label18.Location = new System.Drawing.Point(7, 33);
-			this.label18.Name = "label18";
-			this.label18.Size = new System.Drawing.Size(30, 13);
-			this.label18.TabIndex = 2;
-			this.label18.Text = "Cut:";
+			this.label24.AutoSize = true;
+			this.label24.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label24.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label24.Location = new System.Drawing.Point(7, 72);
+			this.label24.Name = "label24";
+			this.label24.Size = new System.Drawing.Size(65, 13);
+			this.label24.TabIndex = 16;
+			this.label24.Text = "Duplicate:";
+			// 
+			// label21
+			// 
+			this.label21.AutoSize = true;
+			this.label21.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label21.Location = new System.Drawing.Point(85, 59);
+			this.label21.Name = "label21";
+			this.label21.Size = new System.Drawing.Size(41, 13);
+			this.label21.TabIndex = 15;
+			this.label21.Text = "Ctrl + V";
+			// 
+			// label22
+			// 
+			this.label22.AutoSize = true;
+			this.label22.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label22.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label22.Location = new System.Drawing.Point(7, 59);
+			this.label22.Name = "label22";
+			this.label22.Size = new System.Drawing.Size(43, 13);
+			this.label22.TabIndex = 14;
+			this.label22.Text = "Paste:";
 			// 
 			// label19
 			// 
 			this.label19.AutoSize = true;
+			this.label19.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label19.Location = new System.Drawing.Point(85, 46);
 			this.label19.Name = "label19";
 			this.label19.Size = new System.Drawing.Size(41, 13);
@@ -313,61 +356,56 @@
 			// 
 			this.label20.AutoSize = true;
 			this.label20.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label20.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
 			this.label20.Location = new System.Drawing.Point(7, 46);
 			this.label20.Name = "label20";
 			this.label20.Size = new System.Drawing.Size(39, 13);
 			this.label20.TabIndex = 12;
 			this.label20.Text = "Copy:";
 			// 
-			// label21
-			// 
-			this.label21.AutoSize = true;
-			this.label21.Location = new System.Drawing.Point(85, 59);
-			this.label21.Name = "label21";
-			this.label21.Size = new System.Drawing.Size(41, 13);
-			this.label21.TabIndex = 15;
-			this.label21.Text = "Ctrl + V";
-			// 
-			// label22
+			// label17
 			// 
-			this.label22.AutoSize = true;
-			this.label22.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.label22.Location = new System.Drawing.Point(7, 59);
-			this.label22.Name = "label22";
-			this.label22.Size = new System.Drawing.Size(43, 13);
-			this.label22.TabIndex = 14;
-			this.label22.Text = "Paste:";
+			this.label17.AutoSize = true;
+			this.label17.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label17.Location = new System.Drawing.Point(85, 33);
+			this.label17.Name = "label17";
+			this.label17.Size = new System.Drawing.Size(41, 13);
+			this.label17.TabIndex = 3;
+			this.label17.Text = "Ctrl + X";
 			// 
-			// label23
+			// label18
 			// 
-			this.label23.AutoSize = true;
-			this.label23.Location = new System.Drawing.Point(85, 72);
-			this.label23.Name = "label23";
-			this.label23.Size = new System.Drawing.Size(42, 13);
-			this.label23.TabIndex = 17;
-			this.label23.Text = "Ctrl + D";
+			this.label18.AutoSize = true;
+			this.label18.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label18.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label18.Location = new System.Drawing.Point(7, 33);
+			this.label18.Name = "label18";
+			this.label18.Size = new System.Drawing.Size(30, 13);
+			this.label18.TabIndex = 2;
+			this.label18.Text = "Cut:";
 			// 
-			// label24
+			// skinnedPanel1
 			// 
-			this.label24.AutoSize = true;
-			this.label24.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.label24.Location = new System.Drawing.Point(7, 72);
-			this.label24.Name = "label24";
-			this.label24.Size = new System.Drawing.Size(65, 13);
-			this.label24.TabIndex = 16;
-			this.label24.Text = "Duplicate:";
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 296);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(312, 30);
+			this.skinnedPanel1.TabIndex = 5;
 			// 
 			// CanvasHelp
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.ClientSize = new System.Drawing.Size(312, 310);
+			this.ClientSize = new System.Drawing.Size(312, 326);
 			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.groupBox3);
-			this.Controls.Add(this.cmdOK);
 			this.Controls.Add(this.groupBox2);
 			this.Controls.Add(this.groupBox1);
 			this.Name = "CanvasHelp";
+			this.ShowIcon = false;
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Canvas Help";
 			this.groupBox1.ResumeLayout(false);
@@ -376,39 +414,41 @@
 			this.groupBox2.PerformLayout();
 			this.groupBox3.ResumeLayout(false);
 			this.groupBox3.PerformLayout();
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.GroupBox groupBox1;
-		private System.Windows.Forms.Label label4;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.GroupBox groupBox2;
-		private System.Windows.Forms.Label label5;
-		private System.Windows.Forms.Label label6;
-		private System.Windows.Forms.Label label7;
-		private System.Windows.Forms.Label label8;
-		private System.Windows.Forms.Label label13;
-		private System.Windows.Forms.Label label14;
-		private System.Windows.Forms.Label label12;
-		private System.Windows.Forms.Label label11;
-		private System.Windows.Forms.Label label9;
-		private System.Windows.Forms.Label label10;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Label label15;
-		private System.Windows.Forms.Label label16;
-		private System.Windows.Forms.GroupBox groupBox3;
-		private System.Windows.Forms.Label label23;
-		private System.Windows.Forms.Label label24;
-		private System.Windows.Forms.Label label21;
-		private System.Windows.Forms.Label label22;
-		private System.Windows.Forms.Label label19;
-		private System.Windows.Forms.Label label20;
-		private System.Windows.Forms.Label label17;
-		private System.Windows.Forms.Label label18;
+		private Desktop.Skinning.SkinnedGroupBox groupBox1;
+		private Desktop.Skinning.SkinnedLabel label4;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedGroupBox groupBox2;
+		private Desktop.Skinning.SkinnedLabel label5;
+		private Desktop.Skinning.SkinnedLabel label6;
+		private Desktop.Skinning.SkinnedLabel label7;
+		private Desktop.Skinning.SkinnedLabel label8;
+		private Desktop.Skinning.SkinnedLabel label13;
+		private Desktop.Skinning.SkinnedLabel label14;
+		private Desktop.Skinning.SkinnedLabel label12;
+		private Desktop.Skinning.SkinnedLabel label11;
+		private Desktop.Skinning.SkinnedLabel label9;
+		private Desktop.Skinning.SkinnedLabel label10;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedLabel label15;
+		private Desktop.Skinning.SkinnedLabel label16;
+		private Desktop.Skinning.SkinnedGroupBox groupBox3;
+		private Desktop.Skinning.SkinnedLabel label23;
+		private Desktop.Skinning.SkinnedLabel label24;
+		private Desktop.Skinning.SkinnedLabel label21;
+		private Desktop.Skinning.SkinnedLabel label22;
+		private Desktop.Skinning.SkinnedLabel label19;
+		private Desktop.Skinning.SkinnedLabel label20;
+		private Desktop.Skinning.SkinnedLabel label17;
+		private Desktop.Skinning.SkinnedLabel label18;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/CanvasHelp.cs b/editor source/SPNATI Character Editor/Forms/CanvasHelp.cs
index 30a873c788f8bdd8d0e1c6629469a6bfaefb64d6..c6e748e4d2c9bc34290fa4e7187901ab5b78f625 100644
--- a/editor source/SPNATI Character Editor/Forms/CanvasHelp.cs	
+++ b/editor source/SPNATI Character Editor/Forms/CanvasHelp.cs	
@@ -1,9 +1,10 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class CanvasHelp : Form
+	public partial class CanvasHelp : SkinnedForm
 	{
 		public CanvasHelp()
 		{
diff --git a/editor source/SPNATI Character Editor/Forms/CanvasHelp.resx b/editor source/SPNATI Character Editor/Forms/CanvasHelp.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/CanvasHelp.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/ChangeLogReview.Designer.cs b/editor source/SPNATI Character Editor/Forms/ChangeLogReview.Designer.cs
index 29f7156b32ec5f184f1daab753ce5c7fdbc7f660..e8006806eac6cc99c0b6e94d9a194eebb14b60ed 100644
--- a/editor source/SPNATI Character Editor/Forms/ChangeLogReview.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/ChangeLogReview.Designer.cs	
@@ -28,19 +28,24 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.lstVersions = new System.Windows.Forms.ListBox();
+			this.lstVersions = new Desktop.Skinning.SkinnedListBox();
 			this.wb = new System.Windows.Forms.WebBrowser();
-			this.cmdOK = new System.Windows.Forms.Button();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// lstVersions
 			// 
 			this.lstVersions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left)));
+			this.lstVersions.BackColor = System.Drawing.Color.White;
+			this.lstVersions.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstVersions.ForeColor = System.Drawing.Color.Black;
 			this.lstVersions.FormattingEnabled = true;
-			this.lstVersions.Location = new System.Drawing.Point(12, 12);
+			this.lstVersions.Location = new System.Drawing.Point(12, 38);
 			this.lstVersions.Name = "lstVersions";
-			this.lstVersions.Size = new System.Drawing.Size(158, 654);
+			this.lstVersions.Size = new System.Drawing.Size(158, 602);
 			this.lstVersions.TabIndex = 0;
 			this.lstVersions.SelectedIndexChanged += new System.EventHandler(this.lstVersions_SelectedIndexChanged);
 			// 
@@ -49,17 +54,20 @@
 			this.wb.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.wb.Location = new System.Drawing.Point(176, 12);
+			this.wb.Location = new System.Drawing.Point(176, 38);
 			this.wb.MinimumSize = new System.Drawing.Size(20, 20);
 			this.wb.Name = "wb";
-			this.wb.Size = new System.Drawing.Size(905, 629);
+			this.wb.Size = new System.Drawing.Size(905, 603);
 			this.wb.TabIndex = 1;
 			this.wb.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.wb_DocumentCompleted);
 			// 
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(1006, 647);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.Location = new System.Drawing.Point(1014, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 2;
@@ -67,13 +75,23 @@
 			this.cmdOK.UseVisualStyleBackColor = true;
 			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 646);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(1093, 30);
+			this.skinnedPanel1.TabIndex = 3;
+			// 
 			// ChangeLogReview
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.ClientSize = new System.Drawing.Size(1093, 676);
-			this.Controls.Add(this.cmdOK);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.wb);
 			this.Controls.Add(this.lstVersions);
 			this.Name = "ChangeLogReview";
@@ -81,14 +99,16 @@
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Change Log Review";
 			this.Load += new System.EventHandler(this.ChangeLogReview_Load);
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.ListBox lstVersions;
+		private Desktop.Skinning.SkinnedListBox lstVersions;
 		private System.Windows.Forms.WebBrowser wb;
-		private System.Windows.Forms.Button cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/ChangeLogReview.cs b/editor source/SPNATI Character Editor/Forms/ChangeLogReview.cs
index 74fdab218e22517ef6491800f956e188a5e4d811..88507185c8f22330bcbdc3e174342f9f69098022 100644
--- a/editor source/SPNATI Character Editor/Forms/ChangeLogReview.cs	
+++ b/editor source/SPNATI Character Editor/Forms/ChangeLogReview.cs	
@@ -1,11 +1,12 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.IO;
 using System.Text;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class ChangeLogReview : Form
+	public partial class ChangeLogReview : SkinnedForm
 	{
 		public ChangeLogReview()
 		{
diff --git a/editor source/SPNATI Character Editor/Forms/CreateSequenceForm.Designer.cs b/editor source/SPNATI Character Editor/Forms/CreateSequenceForm.Designer.cs
index a7839b6305b746405f0e7846b1097f98c2bf2394..098554ab61038b89060899058679145bc026e6ee 100644
--- a/editor source/SPNATI Character Editor/Forms/CreateSequenceForm.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/CreateSequenceForm.Designer.cs	
@@ -29,17 +29,19 @@
 		private void InitializeComponent()
 		{
 			this.picPreview = new System.Windows.Forms.PictureBox();
-			this.radConvert = new System.Windows.Forms.RadioButton();
-			this.radCreate = new System.Windows.Forms.RadioButton();
-			this.label1 = new System.Windows.Forms.Label();
-			this.lstFrames = new System.Windows.Forms.ListBox();
+			this.radConvert = new Desktop.Skinning.SkinnedRadioButton();
+			this.radCreate = new Desktop.Skinning.SkinnedRadioButton();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.lstFrames = new Desktop.Skinning.SkinnedListBox();
 			this.toolStrip1 = new System.Windows.Forms.ToolStrip();
 			this.tsAdd = new System.Windows.Forms.ToolStripButton();
 			this.tsRemove = new System.Windows.Forms.ToolStripButton();
-			this.label2 = new System.Windows.Forms.Label();
-			this.valTime = new System.Windows.Forms.NumericUpDown();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.valTime = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.txtName = new Desktop.Skinning.SkinnedTextBox();
 			this.openDialog = new SPNATI_Character_Editor.Controls.CharacterImageDialog();
 			((System.ComponentModel.ISupportInitialize)(this.picPreview)).BeginInit();
 			this.toolStrip1.SuspendLayout();
@@ -51,9 +53,9 @@
 			this.picPreview.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left)));
 			this.picPreview.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
-			this.picPreview.Location = new System.Drawing.Point(12, 12);
+			this.picPreview.Location = new System.Drawing.Point(12, 32);
 			this.picPreview.Name = "picPreview";
-			this.picPreview.Size = new System.Drawing.Size(96, 225);
+			this.picPreview.Size = new System.Drawing.Size(96, 248);
 			this.picPreview.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
 			this.picPreview.TabIndex = 0;
 			this.picPreview.TabStop = false;
@@ -61,7 +63,7 @@
 			// radConvert
 			// 
 			this.radConvert.AutoSize = true;
-			this.radConvert.Location = new System.Drawing.Point(116, 12);
+			this.radConvert.Location = new System.Drawing.Point(114, 32);
 			this.radConvert.Name = "radConvert";
 			this.radConvert.Size = new System.Drawing.Size(165, 17);
 			this.radConvert.TabIndex = 1;
@@ -74,7 +76,7 @@
 			// 
 			this.radCreate.AutoSize = true;
 			this.radCreate.Checked = true;
-			this.radCreate.Location = new System.Drawing.Point(116, 35);
+			this.radCreate.Location = new System.Drawing.Point(114, 55);
 			this.radCreate.Name = "radCreate";
 			this.radCreate.Size = new System.Drawing.Size(133, 17);
 			this.radCreate.TabIndex = 2;
@@ -86,7 +88,8 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(116, 59);
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(114, 77);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(44, 13);
 			this.label1.TabIndex = 3;
@@ -96,10 +99,13 @@
 			// 
 			this.lstFrames.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left)));
+			this.lstFrames.BackColor = System.Drawing.Color.White;
+			this.lstFrames.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstFrames.ForeColor = System.Drawing.Color.Black;
 			this.lstFrames.FormattingEnabled = true;
-			this.lstFrames.Location = new System.Drawing.Point(116, 103);
+			this.lstFrames.Location = new System.Drawing.Point(116, 120);
 			this.lstFrames.Name = "lstFrames";
-			this.lstFrames.Size = new System.Drawing.Size(139, 134);
+			this.lstFrames.Size = new System.Drawing.Size(139, 160);
 			this.lstFrames.TabIndex = 4;
 			this.lstFrames.SelectedIndexChanged += new System.EventHandler(this.lstFrames_SelectedIndexChanged);
 			// 
@@ -111,10 +117,11 @@
 			this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.tsAdd,
             this.tsRemove});
-			this.toolStrip1.Location = new System.Drawing.Point(116, 77);
+			this.toolStrip1.Location = new System.Drawing.Point(116, 95);
 			this.toolStrip1.Name = "toolStrip1";
 			this.toolStrip1.Size = new System.Drawing.Size(139, 25);
 			this.toolStrip1.TabIndex = 5;
+			this.toolStrip1.Tag = "Background";
 			this.toolStrip1.Text = "toolStrip1";
 			// 
 			// tsAdd
@@ -140,7 +147,8 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(264, 59);
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label2.Location = new System.Drawing.Point(261, 116);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(125, 13);
 			this.label2.TabIndex = 6;
@@ -148,13 +156,16 @@
 			// 
 			// valTime
 			// 
+			this.valTime.BackColor = System.Drawing.Color.White;
 			this.valTime.DecimalPlaces = 2;
+			this.valTime.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valTime.ForeColor = System.Drawing.Color.Black;
 			this.valTime.Increment = new decimal(new int[] {
             1,
             0,
             0,
             65536});
-			this.valTime.Location = new System.Drawing.Point(267, 75);
+			this.valTime.Location = new System.Drawing.Point(264, 132);
 			this.valTime.Name = "valTime";
 			this.valTime.Size = new System.Drawing.Size(57, 20);
 			this.valTime.TabIndex = 7;
@@ -167,19 +178,26 @@
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(309, 185);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.Location = new System.Drawing.Point(309, 228);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 8;
-			this.cmdOK.Text = "OK";
+			this.cmdOK.Text = "Create";
 			this.cmdOK.UseVisualStyleBackColor = true;
 			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
 			// 
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(309, 214);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.Blue;
+			this.cmdCancel.Location = new System.Drawing.Point(309, 257);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 9;
@@ -187,6 +205,28 @@
 			this.cmdCancel.UseVisualStyleBackColor = true;
 			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
 			// 
+			// label3
+			// 
+			this.label3.AutoSize = true;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label3.Location = new System.Drawing.Point(261, 77);
+			this.label3.Name = "label3";
+			this.label3.Size = new System.Drawing.Size(38, 13);
+			this.label3.TabIndex = 10;
+			this.label3.Text = "Name:";
+			// 
+			// txtName
+			// 
+			this.txtName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtName.BackColor = System.Drawing.Color.White;
+			this.txtName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtName.ForeColor = System.Drawing.Color.Black;
+			this.txtName.Location = new System.Drawing.Point(264, 93);
+			this.txtName.Name = "txtName";
+			this.txtName.Size = new System.Drawing.Size(117, 20);
+			this.txtName.TabIndex = 11;
+			// 
 			// openDialog
 			// 
 			this.openDialog.Filter = "";
@@ -199,8 +239,10 @@
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(396, 249);
+			this.ClientSize = new System.Drawing.Size(396, 292);
 			this.ControlBox = false;
+			this.Controls.Add(this.txtName);
+			this.Controls.Add(this.label3);
 			this.Controls.Add(this.cmdCancel);
 			this.Controls.Add(this.cmdOK);
 			this.Controls.Add(this.valTime);
@@ -226,17 +268,19 @@
 		#endregion
 
 		private System.Windows.Forms.PictureBox picPreview;
-		private System.Windows.Forms.RadioButton radConvert;
-		private System.Windows.Forms.RadioButton radCreate;
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedRadioButton radConvert;
+		private Desktop.Skinning.SkinnedRadioButton radCreate;
+		private Desktop.Skinning.SkinnedLabel label1;
 		private System.Windows.Forms.ToolStrip toolStrip1;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.NumericUpDown valTime;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.ListBox lstFrames;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedNumericUpDown valTime;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedListBox lstFrames;
 		private System.Windows.Forms.ToolStripButton tsAdd;
 		private System.Windows.Forms.ToolStripButton tsRemove;
 		private Controls.CharacterImageDialog openDialog;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedTextBox txtName;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/CreateSequenceForm.cs b/editor source/SPNATI Character Editor/Forms/CreateSequenceForm.cs
index 575a64b6c645f336883834f40855fd194b6b3eb4..2b811e36f958ed1503d827b891329c8d7748cbf0 100644
--- a/editor source/SPNATI Character Editor/Forms/CreateSequenceForm.cs	
+++ b/editor source/SPNATI Character Editor/Forms/CreateSequenceForm.cs	
@@ -1,11 +1,12 @@
-using SPNATI_Character_Editor.EpilogueEditor;
+using Desktop.Skinning;
+using SPNATI_Character_Editor.EpilogueEditor;
 using System.Collections.Generic;
 using System.IO;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class CreateSequenceForm : Form
+	public partial class CreateSequenceForm : SkinnedForm
 	{
 		public LiveSprite Sprite;
 		private ISkin _character;
@@ -28,6 +29,26 @@ namespace SPNATI_Character_Editor.Forms
 			get { return (float)valTime.Value; }
 		}
 
+		public string SequenceName
+		{
+			get
+			{
+				string name = txtName.Text;
+				if (string.IsNullOrEmpty(name))
+				{
+					if (lstFrames.Items.Count > 0)
+					{
+						name = Path.GetFileNameWithoutExtension(lstFrames.Items[0]?.ToString());
+					}
+					else
+					{
+						name = "sequence";
+					}
+				}
+				return name;
+			}
+		}
+
 		public CreateSequenceForm(ISkin character, LiveSprite sprite) : this()
 		{
 			_character = character;
@@ -140,12 +161,33 @@ namespace SPNATI_Character_Editor.Forms
 		private void radCreate_CheckedChanged(object sender, System.EventArgs e)
 		{
 			lstFrames.Items.Clear();
+			if (txtName.Text == "")
+			{
+				txtName.Text = "New Sequence";
+			}
+			txtName.Enabled = true;
 		}
 
 		private void radConvert_CheckedChanged(object sender, System.EventArgs e)
 		{
 			lstFrames.Items.Clear();
-			CreateFrames(Sprite.Keyframes[0].Src);
+			txtName.Enabled = false;
+			txtName.Text = Sprite.Id;
+			if (Sprite.Keyframes.Count == 1)
+			{
+				CreateFrames((Sprite.Keyframes[0] as LiveSpriteKeyframe).Src);
+			}
+			else
+			{
+				foreach (LiveSpriteKeyframe kf in Sprite.Keyframes)
+				{
+					AddFrame(kf.Src);
+				}
+				if (Sprite.Keyframes.Count > 1)
+				{
+					valTime.Value = (decimal)(Sprite.Keyframes[1].Time - Sprite.Keyframes[0].Time);
+				}
+			}
 			lstFrames.SelectedIndex = 0;
 		}
 
diff --git a/editor source/SPNATI Character Editor/Forms/CropCopier.Designer.cs b/editor source/SPNATI Character Editor/Forms/CropCopier.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fa76673b4e43997281e8f0f87f911b9da8b3a988
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/CropCopier.Designer.cs	
@@ -0,0 +1,165 @@
+namespace SPNATI_Character_Editor.Forms
+{
+	partial class CropCopier
+	{
+		/// <summary>
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary>
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Windows Form Designer generated code
+
+		/// <summary>
+		/// Required method for Designer support - do not modify
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.lstFrom = new Desktop.Skinning.SkinnedListBox();
+			this.lstTo = new Desktop.Skinning.SkinnedCheckedListBox();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// label1
+			// 
+			this.label1.AutoSize = true;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.label1.Location = new System.Drawing.Point(12, 32);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(60, 13);
+			this.label1.TabIndex = 0;
+			this.label1.Text = "Copy From:";
+			// 
+			// label2
+			// 
+			this.label2.AutoSize = true;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.label2.Location = new System.Drawing.Point(165, 32);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(50, 13);
+			this.label2.TabIndex = 1;
+			this.label2.Text = "Copy To:";
+			// 
+			// lstFrom
+			// 
+			this.lstFrom.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+			this.lstFrom.BackColor = System.Drawing.Color.White;
+			this.lstFrom.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstFrom.ForeColor = System.Drawing.Color.Black;
+			this.lstFrom.FormattingEnabled = true;
+			this.lstFrom.IntegralHeight = false;
+			this.lstFrom.Location = new System.Drawing.Point(12, 55);
+			this.lstFrom.Name = "lstFrom";
+			this.lstFrom.Size = new System.Drawing.Size(150, 244);
+			this.lstFrom.TabIndex = 2;
+			// 
+			// lstTo
+			// 
+			this.lstTo.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstTo.BackColor = System.Drawing.Color.White;
+			this.lstTo.CheckOnClick = true;
+			this.lstTo.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstTo.ForeColor = System.Drawing.Color.Black;
+			this.lstTo.FormattingEnabled = true;
+			this.lstTo.Location = new System.Drawing.Point(168, 55);
+			this.lstTo.MultiColumn = true;
+			this.lstTo.Name = "lstTo";
+			this.lstTo.Size = new System.Drawing.Size(300, 244);
+			this.lstTo.TabIndex = 3;
+			// 
+			// cmdOK
+			// 
+			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(290, 3);
+			this.cmdOK.Name = "cmdOK";
+			this.cmdOK.Size = new System.Drawing.Size(91, 23);
+			this.cmdOK.TabIndex = 4;
+			this.cmdOK.Text = "Copy";
+			this.cmdOK.UseVisualStyleBackColor = true;
+			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
+			// 
+			// cmdCancel
+			// 
+			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(387, 3);
+			this.cmdCancel.Name = "cmdCancel";
+			this.cmdCancel.Size = new System.Drawing.Size(89, 23);
+			this.cmdCancel.TabIndex = 5;
+			this.cmdCancel.Text = "Cancel";
+			this.cmdCancel.UseVisualStyleBackColor = true;
+			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 303);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(479, 30);
+			this.skinnedPanel1.TabIndex = 6;
+			// 
+			// CropCopier
+			// 
+			this.AcceptButton = this.cmdOK;
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.CancelButton = this.cmdCancel;
+			this.ClientSize = new System.Drawing.Size(479, 333);
+			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
+			this.Controls.Add(this.lstTo);
+			this.Controls.Add(this.lstFrom);
+			this.Controls.Add(this.label2);
+			this.Controls.Add(this.label1);
+			this.Name = "CropCopier";
+			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+			this.Text = "Copy Cropping Data";
+			this.skinnedPanel1.ResumeLayout(false);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedListBox lstFrom;
+		private Desktop.Skinning.SkinnedCheckedListBox lstTo;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+	}
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/CropCopier.cs b/editor source/SPNATI Character Editor/Forms/CropCopier.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6d030b12288f45fecb3edaa3ba5c35e65710279c
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/CropCopier.cs	
@@ -0,0 +1,38 @@
+using Desktop.Skinning;
+using KisekaeImporter.ImageImport;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Forms
+{
+	public partial class CropCopier : SkinnedForm
+	{
+		public CropCopier(PoseList list, ImageMetadata selected)
+		{
+			InitializeComponent();
+
+			foreach (ImageMetadata data in list.Poses)
+			{
+				lstFrom.Items.Add(data);
+				lstTo.Items.Add(data);
+			}
+			lstFrom.SelectedItem = selected;
+		}
+
+		private void cmdOK_Click(object sender, System.EventArgs e)
+		{
+			DialogResult = DialogResult.OK;
+			ImageMetadata source = lstFrom.SelectedItem as ImageMetadata;
+			foreach (ImageMetadata dest in lstTo.CheckedItems)
+			{
+				dest.CropInfo = source.CropInfo;
+			}
+			Close();
+		}
+
+		private void cmdCancel_Click(object sender, System.EventArgs e)
+		{
+			DialogResult = DialogResult.Cancel;
+			Close();
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Forms/CropCopier.resx b/editor source/SPNATI Character Editor/Forms/CropCopier.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/CropCopier.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/DialogueLegend.Designer.cs b/editor source/SPNATI Character Editor/Forms/DialogueLegend.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d6dce420ef0f14aa65f3200a0d7c1389ab2c8739
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/DialogueLegend.Designer.cs	
@@ -0,0 +1,164 @@
+namespace SPNATI_Character_Editor.Forms
+{
+	partial class DialogueLegend
+	{
+		/// <summary>
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary>
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Windows Form Designer generated code
+
+		/// <summary>
+		/// Required method for Designer support - do not modify
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.lblBlue = new System.Windows.Forms.Label();
+			this.lblOrange = new System.Windows.Forms.Label();
+			this.lblGray = new System.Windows.Forms.Label();
+			this.lblLightGray = new System.Windows.Forms.Label();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel2 = new Desktop.Skinning.SkinnedPanel();
+			this.lblGreen = new System.Windows.Forms.Label();
+			this.skinnedPanel1.SuspendLayout();
+			this.skinnedPanel2.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// cmdOK
+			// 
+			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(206, 4);
+			this.cmdOK.Name = "cmdOK";
+			this.cmdOK.Size = new System.Drawing.Size(75, 23);
+			this.cmdOK.TabIndex = 0;
+			this.cmdOK.Text = "OK";
+			this.cmdOK.UseVisualStyleBackColor = true;
+			// 
+			// lblBlue
+			// 
+			this.lblBlue.AutoSize = true;
+			this.lblBlue.ForeColor = System.Drawing.Color.Blue;
+			this.lblBlue.Location = new System.Drawing.Point(3, 3);
+			this.lblBlue.Name = "lblBlue";
+			this.lblBlue.Size = new System.Drawing.Size(113, 13);
+			this.lblBlue.TabIndex = 43;
+			this.lblBlue.Text = "Contains Default Lines";
+			// 
+			// lblOrange
+			// 
+			this.lblOrange.AutoSize = true;
+			this.lblOrange.ForeColor = System.Drawing.Color.OrangeRed;
+			this.lblOrange.Location = new System.Drawing.Point(3, 36);
+			this.lblOrange.Name = "lblOrange";
+			this.lblOrange.Size = new System.Drawing.Size(106, 13);
+			this.lblOrange.TabIndex = 44;
+			this.lblOrange.Text = "Unlocks a Collectible";
+			// 
+			// lblGray
+			// 
+			this.lblGray.AutoSize = true;
+			this.lblGray.ForeColor = System.Drawing.Color.Gray;
+			this.lblGray.Location = new System.Drawing.Point(3, 53);
+			this.lblGray.Name = "lblGray";
+			this.lblGray.Size = new System.Drawing.Size(88, 13);
+			this.lblGray.TabIndex = 45;
+			this.lblGray.Text = "Hidden Condition";
+			// 
+			// lblLightGray
+			// 
+			this.lblLightGray.AutoSize = true;
+			this.lblLightGray.ForeColor = System.Drawing.Color.Silver;
+			this.lblLightGray.Location = new System.Drawing.Point(3, 69);
+			this.lblLightGray.Name = "lblLightGray";
+			this.lblLightGray.Size = new System.Drawing.Size(97, 13);
+			this.lblLightGray.TabIndex = 46;
+			this.lblLightGray.Text = "Hidden From Editor";
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+			this.skinnedPanel1.Controls.Add(this.lblGreen);
+			this.skinnedPanel1.Controls.Add(this.lblLightGray);
+			this.skinnedPanel1.Controls.Add(this.lblBlue);
+			this.skinnedPanel1.Controls.Add(this.lblGray);
+			this.skinnedPanel1.Controls.Add(this.lblOrange);
+			this.skinnedPanel1.Location = new System.Drawing.Point(12, 41);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.skinnedPanel1.Size = new System.Drawing.Size(260, 97);
+			this.skinnedPanel1.TabIndex = 47;
+			// 
+			// skinnedPanel2
+			// 
+			this.skinnedPanel2.Controls.Add(this.cmdOK);
+			this.skinnedPanel2.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel2.Location = new System.Drawing.Point(0, 144);
+			this.skinnedPanel2.Name = "skinnedPanel2";
+			this.skinnedPanel2.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel2.Size = new System.Drawing.Size(284, 30);
+			this.skinnedPanel2.TabIndex = 48;
+			// 
+			// lblGreen
+			// 
+			this.lblGreen.AutoSize = true;
+			this.lblGreen.ForeColor = System.Drawing.Color.Green;
+			this.lblGreen.Location = new System.Drawing.Point(3, 19);
+			this.lblGreen.Name = "lblGreen";
+			this.lblGreen.Size = new System.Drawing.Size(95, 13);
+			this.lblGreen.TabIndex = 47;
+			this.lblGreen.Text = "Targeted Dialogue";
+			// 
+			// DialogueLegend
+			// 
+			this.AcceptButton = this.cmdOK;
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.BackColor = System.Drawing.Color.White;
+			this.CancelButton = this.cmdOK;
+			this.ClientSize = new System.Drawing.Size(284, 174);
+			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel2);
+			this.Controls.Add(this.skinnedPanel1);
+			this.Name = "DialogueLegend";
+			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+			this.Text = "Legend";
+			this.skinnedPanel1.ResumeLayout(false);
+			this.skinnedPanel1.PerformLayout();
+			this.skinnedPanel2.ResumeLayout(false);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private System.Windows.Forms.Label lblBlue;
+		private System.Windows.Forms.Label lblOrange;
+		private System.Windows.Forms.Label lblGray;
+		private System.Windows.Forms.Label lblLightGray;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel2;
+		private System.Windows.Forms.Label lblGreen;
+	}
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/DialogueLegend.cs b/editor source/SPNATI Character Editor/Forms/DialogueLegend.cs
new file mode 100644
index 0000000000000000000000000000000000000000..908f819d189b68aa7e0d88cfce4057af9bc30b9a
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/DialogueLegend.cs	
@@ -0,0 +1,22 @@
+using Desktop.Skinning;
+
+namespace SPNATI_Character_Editor.Forms
+{
+	public partial class DialogueLegend : SkinnedForm
+	{
+		public DialogueLegend()
+		{
+			InitializeComponent();
+		}
+
+		protected override void OnUpdateSkin(Skin skin)
+		{
+			base.OnUpdateSkin(skin);
+			lblBlue.ForeColor = skin.Blue;
+			lblGray.ForeColor = skin.Gray;
+			lblGreen.ForeColor = skin.Green;
+			lblLightGray.ForeColor = skin.LightGray;
+			lblOrange.ForeColor = skin.Orange;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Forms/DialogueLegend.resx b/editor source/SPNATI Character Editor/Forms/DialogueLegend.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/DialogueLegend.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/DialogueResponder.cs b/editor source/SPNATI Character Editor/Forms/DialogueResponder.cs
deleted file mode 100644
index 40851527a7034479b8071bb884642077e1a1ed19..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Forms/DialogueResponder.cs	
+++ /dev/null
@@ -1,90 +0,0 @@
-using System.Windows.Forms;
-
-namespace SPNATI_Character_Editor.Forms
-{
-	public partial class DialogueResponder : Form
-	{
-		private Case _sourceCase;
-		private Case _responseCase;
-		private Character _source;
-		private Character _responder;
-
-		public DialogueResponder()
-		{
-			InitializeComponent();
-		}
-
-		public DialogueResponder(Character source, Case sourceCase, Character responder, Case responseCase) : this()
-		{
-			lblResponse.Text = "Response from " + responder;
-			_source = source;
-			_sourceCase = sourceCase;
-			_responder = responder;
-			_responseCase = responseCase;
-			gridSource.SetData(source, sourceCase);
-			responseControl.SetCharacter(responder);
-			responseControl.HighlightRow += ResponseControl_HighlightRow;
-			responseControl.SetCase(new Stage(responseCase.Stages[0]), responseCase);
-		}
-
-		private void cmdJumpToDialogue_Click(object sender, System.EventArgs e)
-		{
-			responseControl.Save();
-			DialogResult = DialogResult.Retry;
-			Close();
-		}
-
-		private void cmdAccept_Click(object sender, System.EventArgs e)
-		{
-			responseControl.Save();
-			DialogResult = DialogResult.OK;
-			Close();
-		}
-
-		private void cmdCancel_Click(object sender, System.EventArgs e)
-		{
-			DialogResult = DialogResult.Cancel;
-			Close();
-		}
-
-		private void gridSource_HighlightRow(object sender, int index)
-		{
-			if (index == -1)
-				return;
-			string image = gridSource.GetImage(index);
-			ImageLibrary lib = ImageLibrary.Get(_source);
-			CharacterImage img = null;
-			img = lib.Find(image);
-			if (img == null)
-			{
-				int stage = _sourceCase.Stages[0];
-				image = DialogueLine.GetStageImage(stage, DialogueLine.GetDefaultImage(image));
-				img = lib.Find(image);
-			}
-			imgSource.SetImage(img);
-		}
-
-
-		private void ResponseControl_HighlightRow(object sender, int line)
-		{
-			if (line == -1)
-				return;
-			string image = responseControl.GetImage(line);
-			ImageLibrary lib = ImageLibrary.Get(_responder);
-			CharacterImage img = null;
-			img = lib.Find(image);
-			if (img == null)
-			{
-				int stage = _responseCase.Stages[0];
-				image = DialogueLine.GetStageImage(stage, DialogueLine.GetDefaultImage(image));
-				img = lib.Find(image);
-			}
-			imgResponse.SetImage(img);
-		}
-
-		private void DialogueResponder_Shown(object sender, System.EventArgs e)
-		{
-			responseControl.Focus();
-		}
-	}
-}
diff --git a/editor source/SPNATI Character Editor/Forms/DiscardResponsePrompt.Designer.cs b/editor source/SPNATI Character Editor/Forms/DiscardResponsePrompt.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..adc62750b69eaaffd16cee90aab3236fc5882fbe
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/DiscardResponsePrompt.Designer.cs	
@@ -0,0 +1,136 @@
+namespace SPNATI_Character_Editor.Forms
+{
+	partial class DiscardResponsePrompt
+	{
+		/// <summary>
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary>
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Windows Form Designer generated code
+
+		/// <summary>
+		/// Required method for Designer support - do not modify
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdAccept = new Desktop.Skinning.SkinnedButton();
+			this.cmdDiscard = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdDiscard);
+			this.skinnedPanel1.Controls.Add(this.cmdAccept);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 63);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(466, 30);
+			this.skinnedPanel1.TabIndex = 0;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.AutoSize = true;
+			this.skinnedLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel1.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel1.Location = new System.Drawing.Point(12, 39);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(246, 13);
+			this.skinnedLabel1.TabIndex = 1;
+			this.skinnedLabel1.Text = "Do you want to save this response before leaving?";
+			// 
+			// cmdAccept
+			// 
+			this.cmdAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdAccept.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdAccept.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdAccept.Flat = false;
+			this.cmdAccept.Location = new System.Drawing.Point(124, 3);
+			this.cmdAccept.Name = "cmdAccept";
+			this.cmdAccept.Size = new System.Drawing.Size(106, 23);
+			this.cmdAccept.TabIndex = 0;
+			this.cmdAccept.Text = "Save && Close";
+			this.cmdAccept.UseVisualStyleBackColor = true;
+			this.cmdAccept.Click += new System.EventHandler(this.cmdAccept_Click);
+			// 
+			// cmdDiscard
+			// 
+			this.cmdDiscard.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdDiscard.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdDiscard.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdDiscard.Flat = false;
+			this.cmdDiscard.Location = new System.Drawing.Point(236, 3);
+			this.cmdDiscard.Name = "cmdDiscard";
+			this.cmdDiscard.Size = new System.Drawing.Size(106, 23);
+			this.cmdDiscard.TabIndex = 1;
+			this.cmdDiscard.Text = "Discard";
+			this.cmdDiscard.UseVisualStyleBackColor = true;
+			this.cmdDiscard.Click += new System.EventHandler(this.cmdDiscard_Click);
+			// 
+			// cmdCancel
+			// 
+			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(348, 3);
+			this.cmdCancel.Name = "cmdCancel";
+			this.cmdCancel.Size = new System.Drawing.Size(106, 23);
+			this.cmdCancel.TabIndex = 2;
+			this.cmdCancel.Text = "Go Back";
+			this.cmdCancel.UseVisualStyleBackColor = true;
+			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
+			// 
+			// DiscardResponsePrompt
+			// 
+			this.AcceptButton = this.cmdAccept;
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.CancelButton = this.cmdCancel;
+			this.ClientSize = new System.Drawing.Size(466, 93);
+			this.ControlBox = false;
+			this.Controls.Add(this.skinnedLabel1);
+			this.Controls.Add(this.skinnedPanel1);
+			this.Name = "DiscardResponsePrompt";
+			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+			this.Text = "Save Response";
+			this.skinnedPanel1.ResumeLayout(false);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
+		private Desktop.Skinning.SkinnedButton cmdDiscard;
+		private Desktop.Skinning.SkinnedButton cmdAccept;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+	}
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/DiscardResponsePrompt.cs b/editor source/SPNATI Character Editor/Forms/DiscardResponsePrompt.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ed3223525ae33492f734cb255da593a7deb5b93b
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/DiscardResponsePrompt.cs	
@@ -0,0 +1,32 @@
+using Desktop.Skinning;
+using System;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Forms
+{
+	public partial class DiscardResponsePrompt : SkinnedForm
+	{
+		public DiscardResponsePrompt()
+		{
+			InitializeComponent();
+		}
+
+		private void cmdAccept_Click(object sender, EventArgs e)
+		{
+			DialogResult = DialogResult.Yes;
+			Close();
+		}
+
+		private void cmdDiscard_Click(object sender, EventArgs e)
+		{
+			DialogResult = DialogResult.No;
+			Close();
+		}
+
+		private void cmdCancel_Click(object sender, EventArgs e)
+		{
+			DialogResult = DialogResult.Cancel;
+			Close();
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Forms/DiscardResponsePrompt.resx b/editor source/SPNATI Character Editor/Forms/DiscardResponsePrompt.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/DiscardResponsePrompt.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/ErrorTrace.Designer.cs b/editor source/SPNATI Character Editor/Forms/ErrorTrace.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..00d0063cd4f536d8aa26e3d3adc7823b4766dbad
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/ErrorTrace.Designer.cs	
@@ -0,0 +1,151 @@
+namespace SPNATI_Character_Editor.Forms
+{
+	partial class ErrorTrace
+	{
+		/// <summary>
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary>
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Windows Form Designer generated code
+
+		/// <summary>
+		/// Required method for Designer support - do not modify
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedLabel2 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedLinkLabel1 = new Desktop.Skinning.SkinnedLinkLabel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdOpen = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.AutoSize = true;
+			this.skinnedLabel1.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.skinnedLabel1.ForeColor = System.Drawing.Color.Red;
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Bad;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.skinnedLabel1.Location = new System.Drawing.Point(12, 34);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(252, 21);
+			this.skinnedLabel1.TabIndex = 0;
+			this.skinnedLabel1.Text = "Sorry! Something just went wrong.";
+			// 
+			// skinnedLabel2
+			// 
+			this.skinnedLabel2.AutoSize = true;
+			this.skinnedLabel2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel2.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.skinnedLabel2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel2.Location = new System.Drawing.Point(9, 117);
+			this.skinnedLabel2.Name = "skinnedLabel2";
+			this.skinnedLabel2.Size = new System.Drawing.Size(320, 13);
+			this.skinnedLabel2.TabIndex = 1;
+			this.skinnedLabel2.Text = "You may want to review the files for any identifying information first.";
+			// 
+			// skinnedLinkLabel1
+			// 
+			this.skinnedLinkLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLinkLabel1.ForeColor = System.Drawing.Color.Black;
+			this.skinnedLinkLabel1.LinkArea = new System.Windows.Forms.LinkArea(27, 4);
+			this.skinnedLinkLabel1.LinkColor = System.Drawing.Color.Blue;
+			this.skinnedLinkLabel1.Location = new System.Drawing.Point(12, 64);
+			this.skinnedLinkLabel1.Name = "skinnedLinkLabel1";
+			this.skinnedLinkLabel1.Size = new System.Drawing.Size(347, 43);
+			this.skinnedLinkLabel1.TabIndex = 2;
+			this.skinnedLinkLabel1.TabStop = true;
+			this.skinnedLinkLabel1.Text = "Details have been recorded here. To help improve this software, please submit cra" +
+    "shdetails.zip to the character_editor_help discord channel.";
+			this.skinnedLinkLabel1.UseCompatibleTextRendering = true;
+			this.skinnedLinkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.skinnedLinkLabel1_LinkClicked);
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdOpen);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 145);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(371, 31);
+			this.skinnedPanel1.TabIndex = 3;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// cmdOK
+			// 
+			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.Location = new System.Drawing.Point(303, 3);
+			this.cmdOK.Name = "cmdOK";
+			this.cmdOK.Size = new System.Drawing.Size(65, 23);
+			this.cmdOK.TabIndex = 0;
+			this.cmdOK.Text = "OK";
+			this.cmdOK.UseVisualStyleBackColor = true;
+			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
+			// 
+			// cmdOpen
+			// 
+			this.cmdOpen.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.cmdOpen.Background = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.cmdOpen.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdOpen.Flat = true;
+			this.cmdOpen.ForeColor = System.Drawing.Color.White;
+			this.cmdOpen.Location = new System.Drawing.Point(3, 3);
+			this.cmdOpen.Name = "cmdOpen";
+			this.cmdOpen.Size = new System.Drawing.Size(109, 23);
+			this.cmdOpen.TabIndex = 1;
+			this.cmdOpen.Text = "Open Folder";
+			this.cmdOpen.UseVisualStyleBackColor = true;
+			this.cmdOpen.Click += new System.EventHandler(this.cmdOpen_Click);
+			// 
+			// ErrorTrace
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.ClientSize = new System.Drawing.Size(371, 176);
+			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
+			this.Controls.Add(this.skinnedLinkLabel1);
+			this.Controls.Add(this.skinnedLabel2);
+			this.Controls.Add(this.skinnedLabel1);
+			this.Name = "ErrorTrace";
+			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+			this.Text = "SPNATI Character Editor";
+			this.skinnedPanel1.ResumeLayout(false);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel2;
+		private Desktop.Skinning.SkinnedLinkLabel skinnedLinkLabel1;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdOpen;
+	}
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/ErrorTrace.cs b/editor source/SPNATI Character Editor/Forms/ErrorTrace.cs
new file mode 100644
index 0000000000000000000000000000000000000000..eee687076ee2612dc41ef575ee8b6a58774ab45e
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/ErrorTrace.cs	
@@ -0,0 +1,55 @@
+using Desktop.Skinning;
+using System;
+using System.Diagnostics;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Forms
+{
+	public partial class ErrorTrace : SkinnedForm
+	{
+		private string _directory;
+
+		public ErrorTrace()
+		{
+			InitializeComponent();
+		}
+
+		public void SetPath(string directory)
+		{
+			_directory = directory;
+		}
+
+		private void cmdOpen_Click(object sender, EventArgs e)
+		{
+			OpenDirectory();
+		}
+
+		private void cmdOK_Click(object sender, EventArgs e)
+		{
+			Close();
+		}
+
+		private void skinnedLinkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+		{
+			OpenDirectory();
+		}
+
+		private void OpenDirectory()
+		{
+			try
+			{
+				ProcessStartInfo startInfo = new ProcessStartInfo()
+				{
+					Arguments = _directory,
+					FileName = "explorer.exe"
+				};
+
+				Process.Start(startInfo);
+			}
+			catch (Exception ex)
+			{
+				MessageBox.Show(ex.Message);
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Forms/ErrorTrace.resx b/editor source/SPNATI Character Editor/Forms/ErrorTrace.resx
new file mode 100644
index 0000000000000000000000000000000000000000..1af7de150c99c12dd67a509fe57c10d63e4eeb04
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/ErrorTrace.resx	
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/FailedImport.Designer.cs b/editor source/SPNATI Character Editor/Forms/FailedImport.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..820fa104fefe5ade1edc7f80856033ba95222380
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/FailedImport.Designer.cs	
@@ -0,0 +1,171 @@
+namespace SPNATI_Character_Editor.Forms
+{
+	partial class FailedImport
+	{
+		/// <summary>
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary>
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Windows Form Designer generated code
+
+		/// <summary>
+		/// Required method for Designer support - do not modify
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FailedImport));
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedLabel2 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.skinnedLabel3 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedLabel4 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedLabel5 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedLabel6 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.AutoSize = true;
+			this.skinnedLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.skinnedLabel1.ForeColor = System.Drawing.Color.Red;
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Bad;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel1.Location = new System.Drawing.Point(12, 37);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(145, 13);
+			this.skinnedLabel1.TabIndex = 0;
+			this.skinnedLabel1.Text = "Failed to import from Kisekae.";
+			// 
+			// skinnedLabel2
+			// 
+			this.skinnedLabel2.AutoSize = true;
+			this.skinnedLabel2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel2.Location = new System.Drawing.Point(12, 59);
+			this.skinnedLabel2.Name = "skinnedLabel2";
+			this.skinnedLabel2.Size = new System.Drawing.Size(305, 13);
+			this.skinnedLabel2.TabIndex = 1;
+			this.skinnedLabel2.Text = "This usually happens because of one of the following problems:";
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 199);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
+			this.skinnedPanel1.Size = new System.Drawing.Size(448, 30);
+			this.skinnedPanel1.TabIndex = 2;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// cmdOK
+			// 
+			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.Location = new System.Drawing.Point(370, 3);
+			this.cmdOK.Name = "cmdOK";
+			this.cmdOK.Size = new System.Drawing.Size(75, 23);
+			this.cmdOK.TabIndex = 0;
+			this.cmdOK.Text = "OK";
+			this.cmdOK.UseVisualStyleBackColor = true;
+			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
+			// 
+			// skinnedLabel3
+			// 
+			this.skinnedLabel3.AutoSize = true;
+			this.skinnedLabel3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel3.Location = new System.Drawing.Point(41, 81);
+			this.skinnedLabel3.Name = "skinnedLabel3";
+			this.skinnedLabel3.Size = new System.Drawing.Size(149, 13);
+			this.skinnedLabel3.TabIndex = 3;
+			this.skinnedLabel3.Text = "1. You are not running kkl.exe";
+			// 
+			// skinnedLabel4
+			// 
+			this.skinnedLabel4.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel4.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel4.Location = new System.Drawing.Point(41, 99);
+			this.skinnedLabel4.Name = "skinnedLabel4";
+			this.skinnedLabel4.Size = new System.Drawing.Size(398, 30);
+			this.skinnedLabel4.TabIndex = 4;
+			this.skinnedLabel4.Text = "2. You are not running the same version of kkl.exe that the editor is configured " +
+    "to communicate with. Check the editor settings.";
+			// 
+			// skinnedLabel5
+			// 
+			this.skinnedLabel5.AutoSize = true;
+			this.skinnedLabel5.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel5.Location = new System.Drawing.Point(41, 131);
+			this.skinnedLabel5.Name = "skinnedLabel5";
+			this.skinnedLabel5.Size = new System.Drawing.Size(236, 13);
+			this.skinnedLabel5.TabIndex = 5;
+			this.skinnedLabel5.Text = "3. You are running from a Mac or Linux machine.";
+			// 
+			// skinnedLabel6
+			// 
+			this.skinnedLabel6.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel6.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel6.Location = new System.Drawing.Point(41, 148);
+			this.skinnedLabel6.Name = "skinnedLabel6";
+			this.skinnedLabel6.Size = new System.Drawing.Size(398, 43);
+			this.skinnedLabel6.TabIndex = 6;
+			this.skinnedLabel6.Text = resources.GetString("skinnedLabel6.Text");
+			// 
+			// FailedImport
+			// 
+			this.AcceptButton = this.cmdOK;
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.CancelButton = this.cmdOK;
+			this.ClientSize = new System.Drawing.Size(448, 229);
+			this.ControlBox = false;
+			this.Controls.Add(this.skinnedLabel6);
+			this.Controls.Add(this.skinnedLabel5);
+			this.Controls.Add(this.skinnedLabel4);
+			this.Controls.Add(this.skinnedLabel3);
+			this.Controls.Add(this.skinnedPanel1);
+			this.Controls.Add(this.skinnedLabel2);
+			this.Controls.Add(this.skinnedLabel1);
+			this.Name = "FailedImport";
+			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+			this.Text = "Import Image";
+			this.skinnedPanel1.ResumeLayout(false);
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel2;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel3;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel4;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel5;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel6;
+	}
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/FailedImport.cs b/editor source/SPNATI Character Editor/Forms/FailedImport.cs
new file mode 100644
index 0000000000000000000000000000000000000000..456c2faff2e2df697ac1aa005085f58878eaba1c
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/FailedImport.cs	
@@ -0,0 +1,18 @@
+using Desktop.Skinning;
+using System;
+
+namespace SPNATI_Character_Editor.Forms
+{
+	public partial class FailedImport : SkinnedForm
+	{
+		public FailedImport()
+		{
+			InitializeComponent();
+		}
+
+		private void cmdOK_Click(object sender, EventArgs e)
+		{
+			Close();
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Forms/FailedImport.resx b/editor source/SPNATI Character Editor/Forms/FailedImport.resx
new file mode 100644
index 0000000000000000000000000000000000000000..3d6ba513e598789094815ee3961b5e09122e6296
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/FailedImport.resx	
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="skinnedLabel6.Text" xml:space="preserve">
+    <value>4. Kisekae's exporter has become corrupted. Try using Tools &gt; Fix Kisekae and trying again. If the problem persists, please post the code you are trying to import on the Discord channel for more assistance.</value>
+  </data>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/FirstLaunchSetup.Designer.cs b/editor source/SPNATI Character Editor/Forms/FirstLaunchSetup.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4cb386ff52a2a408f5b06fb7205042159a049a76
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/FirstLaunchSetup.Designer.cs	
@@ -0,0 +1,272 @@
+namespace SPNATI_Character_Editor.Forms
+{
+	partial class FirstLaunchSetup
+	{
+		/// <summary>
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary>
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Windows Form Designer generated code
+
+		/// <summary>
+		/// Required method for Designer support - do not modify
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.skinnedLabel1 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdBrowseKisekae = new Desktop.Skinning.SkinnedButton();
+			this.txtKisekae = new Desktop.Skinning.SkinnedTextBox();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdBrowse = new Desktop.Skinning.SkinnedButton();
+			this.txtApplicationDirectory = new Desktop.Skinning.SkinnedTextBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.txtUserName = new Desktop.Skinning.SkinnedTextBox();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.folderBrowserDialog1 = new System.Windows.Forms.FolderBrowserDialog();
+			this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
+			this.skinnedLabel2 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// skinnedLabel1
+			// 
+			this.skinnedLabel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedLabel1.Font = new System.Drawing.Font("Segoe UI", 12F);
+			this.skinnedLabel1.ForeColor = System.Drawing.Color.Blue;
+			this.skinnedLabel1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel1.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.skinnedLabel1.Location = new System.Drawing.Point(12, 35);
+			this.skinnedLabel1.Name = "skinnedLabel1";
+			this.skinnedLabel1.Size = new System.Drawing.Size(580, 49);
+			this.skinnedLabel1.TabIndex = 0;
+			this.skinnedLabel1.Text = "Welcome to the SPNATI Character Editor! As this is your first time running this p" +
+    "rogram, we need a few bits of information.";
+			// 
+			// cmdBrowseKisekae
+			// 
+			this.cmdBrowseKisekae.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdBrowseKisekae.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdBrowseKisekae.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdBrowseKisekae.Flat = false;
+			this.cmdBrowseKisekae.Location = new System.Drawing.Point(560, 111);
+			this.cmdBrowseKisekae.Name = "cmdBrowseKisekae";
+			this.cmdBrowseKisekae.Size = new System.Drawing.Size(32, 23);
+			this.cmdBrowseKisekae.TabIndex = 14;
+			this.cmdBrowseKisekae.Text = "...";
+			this.cmdBrowseKisekae.UseVisualStyleBackColor = true;
+			this.cmdBrowseKisekae.Click += new System.EventHandler(this.cmdBrowseKisekae_Click);
+			// 
+			// txtKisekae
+			// 
+			this.txtKisekae.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtKisekae.BackColor = System.Drawing.Color.White;
+			this.txtKisekae.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtKisekae.ForeColor = System.Drawing.Color.Black;
+			this.txtKisekae.Location = new System.Drawing.Point(139, 113);
+			this.txtKisekae.Name = "txtKisekae";
+			this.txtKisekae.Size = new System.Drawing.Size(415, 20);
+			this.txtKisekae.TabIndex = 13;
+			// 
+			// label5
+			// 
+			this.label5.AutoSize = true;
+			this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label5.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label5.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label5.Location = new System.Drawing.Point(13, 116);
+			this.label5.Name = "label5";
+			this.label5.Size = new System.Drawing.Size(90, 13);
+			this.label5.TabIndex = 17;
+			this.label5.Text = "KKL.exe location:";
+			// 
+			// cmdBrowse
+			// 
+			this.cmdBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdBrowse.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdBrowse.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdBrowse.Flat = false;
+			this.cmdBrowse.Location = new System.Drawing.Point(560, 85);
+			this.cmdBrowse.Name = "cmdBrowse";
+			this.cmdBrowse.Size = new System.Drawing.Size(32, 23);
+			this.cmdBrowse.TabIndex = 11;
+			this.cmdBrowse.Text = "...";
+			this.cmdBrowse.UseVisualStyleBackColor = true;
+			this.cmdBrowse.Click += new System.EventHandler(this.cmdBrowse_Click);
+			// 
+			// txtApplicationDirectory
+			// 
+			this.txtApplicationDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtApplicationDirectory.BackColor = System.Drawing.Color.White;
+			this.txtApplicationDirectory.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtApplicationDirectory.ForeColor = System.Drawing.Color.Black;
+			this.txtApplicationDirectory.Location = new System.Drawing.Point(139, 87);
+			this.txtApplicationDirectory.Name = "txtApplicationDirectory";
+			this.txtApplicationDirectory.Size = new System.Drawing.Size(415, 20);
+			this.txtApplicationDirectory.TabIndex = 10;
+			this.txtApplicationDirectory.Validating += new System.ComponentModel.CancelEventHandler(this.txtApplicationDirectory_Validating);
+			// 
+			// label1
+			// 
+			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(12, 90);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(97, 13);
+			this.label1.TabIndex = 12;
+			this.label1.Text = "SPNATI repository:";
+			// 
+			// txtUserName
+			// 
+			this.txtUserName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtUserName.BackColor = System.Drawing.Color.White;
+			this.txtUserName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtUserName.ForeColor = System.Drawing.Color.Black;
+			this.txtUserName.Location = new System.Drawing.Point(139, 139);
+			this.txtUserName.Name = "txtUserName";
+			this.txtUserName.Size = new System.Drawing.Size(238, 20);
+			this.txtUserName.TabIndex = 15;
+			// 
+			// label2
+			// 
+			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label2.Location = new System.Drawing.Point(13, 142);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(58, 13);
+			this.label2.TabIndex = 16;
+			this.label2.Text = "Username:";
+			// 
+			// cmdOK
+			// 
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.Location = new System.Drawing.Point(445, 3);
+			this.cmdOK.Name = "cmdOK";
+			this.cmdOK.Size = new System.Drawing.Size(75, 23);
+			this.cmdOK.TabIndex = 18;
+			this.cmdOK.Text = "Accept";
+			this.cmdOK.UseVisualStyleBackColor = true;
+			this.cmdOK.Click += new System.EventHandler(this.cmdOk_Click);
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.skinnedLabel2);
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 171);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(604, 30);
+			this.skinnedPanel1.TabIndex = 19;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// cmdCancel
+			// 
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(526, 4);
+			this.cmdCancel.Name = "cmdCancel";
+			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
+			this.cmdCancel.TabIndex = 19;
+			this.cmdCancel.Text = "Cancel";
+			this.cmdCancel.UseVisualStyleBackColor = true;
+			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
+			// 
+			// openFileDialog1
+			// 
+			this.openFileDialog1.FileName = "openFileDialog1";
+			this.openFileDialog1.Filter = "Exe files|*.exe";
+			// 
+			// skinnedLabel2
+			// 
+			this.skinnedLabel2.AutoSize = true;
+			this.skinnedLabel2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.skinnedLabel2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.skinnedLabel2.Location = new System.Drawing.Point(4, 8);
+			this.skinnedLabel2.Name = "skinnedLabel2";
+			this.skinnedLabel2.Size = new System.Drawing.Size(431, 13);
+			this.skinnedLabel2.TabIndex = 20;
+			this.skinnedLabel2.Text = "To change these and other settings later, use the Gear in the upper right of the " +
+    "main editor";
+			// 
+			// FirstLaunchSetup
+			// 
+			this.AcceptButton = this.cmdOK;
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.CancelButton = this.cmdCancel;
+			this.ClientSize = new System.Drawing.Size(604, 201);
+			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
+			this.Controls.Add(this.cmdBrowseKisekae);
+			this.Controls.Add(this.txtKisekae);
+			this.Controls.Add(this.label5);
+			this.Controls.Add(this.cmdBrowse);
+			this.Controls.Add(this.txtApplicationDirectory);
+			this.Controls.Add(this.label1);
+			this.Controls.Add(this.txtUserName);
+			this.Controls.Add(this.label2);
+			this.Controls.Add(this.skinnedLabel1);
+			this.Name = "FirstLaunchSetup";
+			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+			this.Text = "Welcome";
+			this.skinnedPanel1.ResumeLayout(false);
+			this.skinnedPanel1.PerformLayout();
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedLabel skinnedLabel1;
+		private Desktop.Skinning.SkinnedButton cmdBrowseKisekae;
+		private Desktop.Skinning.SkinnedTextBox txtKisekae;
+		private Desktop.Skinning.SkinnedLabel label5;
+		private Desktop.Skinning.SkinnedButton cmdBrowse;
+		private Desktop.Skinning.SkinnedTextBox txtApplicationDirectory;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedTextBox txtUserName;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private System.Windows.Forms.FolderBrowserDialog folderBrowserDialog1;
+		private System.Windows.Forms.OpenFileDialog openFileDialog1;
+		private Desktop.Skinning.SkinnedLabel skinnedLabel2;
+	}
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/FirstLaunchSetup.cs b/editor source/SPNATI Character Editor/Forms/FirstLaunchSetup.cs
new file mode 100644
index 0000000000000000000000000000000000000000..989f3786f00db07c73d7f1960460e1c12ca74524
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/FirstLaunchSetup.cs	
@@ -0,0 +1,125 @@
+using Desktop;
+using Desktop.Skinning;
+using System;
+using System.IO;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Forms
+{
+	public partial class FirstLaunchSetup : SkinnedForm
+	{
+		public FirstLaunchSetup()
+		{
+			InitializeComponent();
+			string gameDir = Config.GetString(Settings.GameDirectory);
+			txtApplicationDirectory.Text = gameDir;
+			txtKisekae.Text = Config.KisekaeDirectory;
+			folderBrowserDialog1.SelectedPath = gameDir;
+			txtUserName.Text = Config.UserName;
+		}
+
+		private void cmdBrowse_Click(object sender, System.EventArgs e)
+		{
+			if (!string.IsNullOrEmpty(txtApplicationDirectory.Text))
+			{
+				try
+				{
+					folderBrowserDialog1.SelectedPath = txtApplicationDirectory.Text;
+				}
+				catch { }
+			}
+			if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
+			{
+				txtApplicationDirectory.Text = folderBrowserDialog1.SelectedPath;
+				ValidateApplicationDirectory();
+			}
+		}
+
+		private void ValidateApplicationDirectory()
+		{
+			if (string.IsNullOrEmpty(txtApplicationDirectory.Text))
+			{
+				return;
+			}
+
+			//try to make things easier for people who got close but not perfect by auto-adjusting
+			string original = Path.GetFullPath(txtApplicationDirectory.Text);
+			if (!string.IsNullOrEmpty(original) && original.EndsWith("\\"))
+			{
+				original = original.Substring(0, original.Length - 1);
+			}
+			string dir = original;
+			if (!SettingsSetup.VerifyApplicationDirectory(dir))
+			{
+				//try going up a level
+				dir = Path.GetDirectoryName(original);
+			}
+			if (!SettingsSetup.VerifyApplicationDirectory(dir))
+			{
+				//Nope. How about down a level?
+				bool succeed = false;
+				foreach (string subfolder in Directory.EnumerateDirectories(original))
+				{
+					if (SettingsSetup.VerifyApplicationDirectory(subfolder))
+					{
+						dir = subfolder;
+						succeed = true;
+						break;
+					}
+				}
+				if (!succeed)
+				{
+					dir = original;
+				}
+			}
+			if (dir != txtApplicationDirectory.Text)
+			{
+				txtApplicationDirectory.Text = dir;
+			}
+		}
+
+		private void cmdBrowseKisekae_Click(object sender, System.EventArgs e)
+		{
+			if (!string.IsNullOrEmpty(txtKisekae.Text))
+			{
+				openFileDialog1.InitialDirectory = Path.GetDirectoryName(txtKisekae.Text);
+				openFileDialog1.FileName = Path.GetFileName(txtKisekae.Text);
+			}
+			if (openFileDialog1.ShowDialog() == DialogResult.OK)
+			{
+				string file = openFileDialog1.FileName;
+				txtKisekae.Text = Path.GetFullPath(file);
+			}
+		}
+
+		private void txtApplicationDirectory_Validating(object sender, System.ComponentModel.CancelEventArgs e)
+		{
+			ValidateApplicationDirectory();
+		}
+
+		private void cmdCancel_Click(object sender, EventArgs e)
+		{
+			DialogResult = DialogResult.Cancel;
+			Close();
+		}
+
+		private void cmdOk_Click(object sender, EventArgs e)
+		{
+			string dir = txtApplicationDirectory.Text;
+			if (!SettingsSetup.VerifyApplicationDirectory(dir, true))
+			{
+				MessageBox.Show("The provided directory does not appear to contain SPNATI! This application cannot start without a valid directory.");
+				return;
+			}
+			Config.Set(Settings.GameDirectory, dir);
+			
+			Config.UserName = txtUserName.Text;
+			Config.KisekaeDirectory = txtKisekae.Text;
+			
+			DialogResult = DialogResult.OK;
+			Config.Save();
+			Shell.Instance.PostOffice.SendMessage(DesktopMessages.SettingsUpdated);
+			Close();
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Forms/FirstLaunchSetup.resx b/editor source/SPNATI Character Editor/Forms/FirstLaunchSetup.resx
new file mode 100644
index 0000000000000000000000000000000000000000..20bdf5e08d61ef99a3948cbd93ec4d38e0867848
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/FirstLaunchSetup.resx	
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="folderBrowserDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>326, 17</value>
+  </metadata>
+  <metadata name="openFileDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>495, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/GameConfig.Designer.cs b/editor source/SPNATI Character Editor/Forms/GameConfig.Designer.cs
index 20b391744f529adc7acb1b44fd16b690dcd087aa..5ae29140cfe914a6ef370f597401768270b3e6b8 100644
--- a/editor source/SPNATI Character Editor/Forms/GameConfig.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/GameConfig.Designer.cs	
@@ -28,17 +28,19 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.chkDebug = new System.Windows.Forms.CheckBox();
-			this.chkEpilogues = new System.Windows.Forms.CheckBox();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.chkCollectibles = new System.Windows.Forms.CheckBox();
+			this.chkDebug = new Desktop.Skinning.SkinnedCheckBox();
+			this.chkEpilogues = new Desktop.Skinning.SkinnedCheckBox();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.chkCollectibles = new Desktop.Skinning.SkinnedCheckBox();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// chkDebug
 			// 
 			this.chkDebug.AutoSize = true;
-			this.chkDebug.Location = new System.Drawing.Point(12, 12);
+			this.chkDebug.Location = new System.Drawing.Point(12, 42);
 			this.chkDebug.Name = "chkDebug";
 			this.chkDebug.Size = new System.Drawing.Size(405, 17);
 			this.chkDebug.TabIndex = 0;
@@ -48,7 +50,7 @@
 			// chkEpilogues
 			// 
 			this.chkEpilogues.AutoSize = true;
-			this.chkEpilogues.Location = new System.Drawing.Point(12, 35);
+			this.chkEpilogues.Location = new System.Drawing.Point(12, 65);
 			this.chkEpilogues.Name = "chkEpilogues";
 			this.chkEpilogues.Size = new System.Drawing.Size(305, 17);
 			this.chkEpilogues.TabIndex = 1;
@@ -58,7 +60,11 @@
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(260, 158);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(269, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 2;
@@ -69,8 +75,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(341, 158);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(350, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 3;
@@ -81,13 +91,24 @@
 			// chkCollectibles
 			// 
 			this.chkCollectibles.AutoSize = true;
-			this.chkCollectibles.Location = new System.Drawing.Point(12, 58);
+			this.chkCollectibles.Location = new System.Drawing.Point(12, 88);
 			this.chkCollectibles.Name = "chkCollectibles";
 			this.chkCollectibles.Size = new System.Drawing.Size(312, 17);
 			this.chkCollectibles.TabIndex = 4;
 			this.chkCollectibles.Text = "Unlock Collectibles (allows viewing collectibles in the Gallery)";
 			this.chkCollectibles.UseVisualStyleBackColor = true;
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 163);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(428, 30);
+			this.skinnedPanel1.TabIndex = 5;
+			// 
 			// GameConfig
 			// 
 			this.AcceptButton = this.cmdOK;
@@ -96,15 +117,16 @@
 			this.CancelButton = this.cmdCancel;
 			this.ClientSize = new System.Drawing.Size(428, 193);
 			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.chkCollectibles);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
 			this.Controls.Add(this.chkEpilogues);
 			this.Controls.Add(this.chkDebug);
 			this.Name = "GameConfig";
+			this.ShowIcon = false;
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Game Configuration";
 			this.Load += new System.EventHandler(this.GameConfig_Load);
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -112,10 +134,11 @@
 
 		#endregion
 
-		private System.Windows.Forms.CheckBox chkDebug;
-		private System.Windows.Forms.CheckBox chkEpilogues;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.CheckBox chkCollectibles;
+		private Desktop.Skinning.SkinnedCheckBox chkDebug;
+		private Desktop.Skinning.SkinnedCheckBox chkEpilogues;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedCheckBox chkCollectibles;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/GameConfig.cs b/editor source/SPNATI Character Editor/Forms/GameConfig.cs
index 7ef14479a25db3fce3ae0365c13f13a8a03310ed..82c842daff910721f16f5f352a222e25b5c3de0a 100644
--- a/editor source/SPNATI Character Editor/Forms/GameConfig.cs	
+++ b/editor source/SPNATI Character Editor/Forms/GameConfig.cs	
@@ -1,9 +1,10 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class GameConfig : Form
+	public partial class GameConfig : SkinnedForm
 	{
 		public GameConfig()
 		{
diff --git a/editor source/SPNATI Character Editor/Forms/ImageCropper.Designer.cs b/editor source/SPNATI Character Editor/Forms/ImageCropper.Designer.cs
index ae53e1ae08d3760274546a01fc6267e0f554d4c4..c85c1356c9b8ff0dabaec5402b74bd42840b2c5c 100644
--- a/editor source/SPNATI Character Editor/Forms/ImageCropper.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/ImageCropper.Designer.cs	
@@ -30,42 +30,49 @@
 		{
 			this.components = new System.ComponentModel.Container();
 			System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ImageCropper));
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.label7 = new System.Windows.Forms.Label();
-			this.label6 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
-			this.label1 = new System.Windows.Forms.Label();
-			this.previewPanel = new Desktop.CommonControls.DBPanel();
-			this.lblWait = new System.Windows.Forms.Label();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.label7 = new Desktop.Skinning.SkinnedLabel();
+			this.label6 = new Desktop.Skinning.SkinnedLabel();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
 			this.tmrWait = new System.Windows.Forms.Timer(this.components);
-			this.label3 = new System.Windows.Forms.Label();
-			this.panelManual = new System.Windows.Forms.Panel();
-			this.label4 = new System.Windows.Forms.Label();
-			this.cmdReimport = new System.Windows.Forms.Button();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.panelManual = new Desktop.Skinning.SkinnedPanel();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdReimport = new Desktop.Skinning.SkinnedButton();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
-			this.label5 = new System.Windows.Forms.Label();
-			this.label8 = new System.Windows.Forms.Label();
-			this.label9 = new System.Windows.Forms.Label();
-			this.label10 = new System.Windows.Forms.Label();
-			this.valLeft = new System.Windows.Forms.NumericUpDown();
-			this.valTop = new System.Windows.Forms.NumericUpDown();
-			this.valBottom = new System.Windows.Forms.NumericUpDown();
-			this.valRight = new System.Windows.Forms.NumericUpDown();
-			this.chkNoCrop = new System.Windows.Forms.CheckBox();
-			this.cmdAdvanced = new System.Windows.Forms.Button();
-			this.previewPanel.SuspendLayout();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
+			this.label8 = new Desktop.Skinning.SkinnedLabel();
+			this.label9 = new Desktop.Skinning.SkinnedLabel();
+			this.label10 = new Desktop.Skinning.SkinnedLabel();
+			this.valLeft = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.valTop = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.valBottom = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.valRight = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.chkNoCrop = new Desktop.Skinning.SkinnedCheckBox();
+			this.cmdAdvanced = new Desktop.Skinning.SkinnedButton();
+			this.previewPanel = new Desktop.CommonControls.DBPanel();
+			this.lblWait = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
 			this.panelManual.SuspendLayout();
 			((System.ComponentModel.ISupportInitialize)(this.valLeft)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.valTop)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.valBottom)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.valRight)).BeginInit();
+			this.previewPanel.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// cmdCancel
 			// 
+			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(603, 517);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(829, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 5;
@@ -75,18 +82,26 @@
 			// 
 			// cmdOK
 			// 
-			this.cmdOK.Location = new System.Drawing.Point(522, 517);
+			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.Location = new System.Drawing.Point(748, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 4;
-			this.cmdOK.Text = "OK";
+			this.cmdOK.Text = "Crop";
 			this.cmdOK.UseVisualStyleBackColor = true;
 			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
 			// 
 			// label7
 			// 
 			this.label7.AutoSize = true;
-			this.label7.Location = new System.Drawing.Point(698, 51);
+			this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label7.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label7.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label7.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label7.Location = new System.Drawing.Point(698, 73);
 			this.label7.Name = "label7";
 			this.label7.Size = new System.Drawing.Size(207, 13);
 			this.label7.TabIndex = 22;
@@ -95,7 +110,11 @@
 			// label6
 			// 
 			this.label6.AutoSize = true;
-			this.label6.Location = new System.Drawing.Point(698, 38);
+			this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label6.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label6.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label6.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label6.Location = new System.Drawing.Point(698, 60);
 			this.label6.Name = "label6";
 			this.label6.Size = new System.Drawing.Size(195, 13);
 			this.label6.TabIndex = 21;
@@ -104,7 +123,11 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(698, 25);
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label2.Location = new System.Drawing.Point(698, 47);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(187, 13);
 			this.label2.TabIndex = 20;
@@ -113,35 +136,16 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(684, 12);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(684, 34);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(130, 13);
 			this.label1.TabIndex = 19;
 			this.label1.Text = "Cropping Box Instructions:";
 			// 
-			// previewPanel
-			// 
-			this.previewPanel.BackColor = System.Drawing.SystemColors.ActiveCaption;
-			this.previewPanel.Controls.Add(this.lblWait);
-			this.previewPanel.Location = new System.Drawing.Point(12, 12);
-			this.previewPanel.Name = "previewPanel";
-			this.previewPanel.Size = new System.Drawing.Size(666, 500);
-			this.previewPanel.TabIndex = 3;
-			this.previewPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.previewPanel_Paint);
-			this.previewPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.previewPanel_MouseDown);
-			this.previewPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.previewPanel_MouseMove);
-			this.previewPanel.MouseUp += new System.Windows.Forms.MouseEventHandler(this.previewPanel_MouseUp);
-			// 
-			// lblWait
-			// 
-			this.lblWait.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblWait.Location = new System.Drawing.Point(3, 237);
-			this.lblWait.Name = "lblWait";
-			this.lblWait.Size = new System.Drawing.Size(660, 13);
-			this.lblWait.TabIndex = 0;
-			this.lblWait.Text = "Please wait";
-			this.lblWait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
-			// 
 			// tmrWait
 			// 
 			this.tmrWait.Interval = 250;
@@ -150,11 +154,13 @@
 			// label3
 			// 
 			this.label3.AutoSize = true;
-			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
 			this.label3.ForeColor = System.Drawing.Color.MediumBlue;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label3.Location = new System.Drawing.Point(3, 1);
 			this.label3.Name = "label3";
-			this.label3.Size = new System.Drawing.Size(73, 31);
+			this.label3.Size = new System.Drawing.Size(30, 13);
 			this.label3.TabIndex = 23;
 			this.label3.Text = "Uh...";
 			// 
@@ -163,14 +169,20 @@
 			this.panelManual.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
 			this.panelManual.Controls.Add(this.label4);
 			this.panelManual.Controls.Add(this.label3);
-			this.panelManual.Location = new System.Drawing.Point(687, 284);
+			this.panelManual.Location = new System.Drawing.Point(687, 304);
 			this.panelManual.Name = "panelManual";
+			this.panelManual.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
 			this.panelManual.Size = new System.Drawing.Size(210, 171);
 			this.panelManual.TabIndex = 24;
+			this.panelManual.TabSide = Desktop.Skinning.TabSide.None;
 			this.panelManual.Visible = false;
 			// 
 			// label4
 			// 
+			this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label4.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label4.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label4.Location = new System.Drawing.Point(11, 31);
 			this.label4.Name = "label4";
 			this.label4.Size = new System.Drawing.Size(187, 125);
@@ -179,9 +191,12 @@
 			// 
 			// cmdReimport
 			// 
-			this.cmdReimport.Location = new System.Drawing.Point(696, 489);
+			this.cmdReimport.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdReimport.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdReimport.Flat = false;
+			this.cmdReimport.Location = new System.Drawing.Point(687, 511);
 			this.cmdReimport.Name = "cmdReimport";
-			this.cmdReimport.Size = new System.Drawing.Size(189, 23);
+			this.cmdReimport.Size = new System.Drawing.Size(210, 23);
 			this.cmdReimport.TabIndex = 26;
 			this.cmdReimport.Text = "Reimport";
 			this.toolTip1.SetToolTip(this.cmdReimport, "Recapture whatever is currently visible in Kisekae");
@@ -191,7 +206,11 @@
 			// label5
 			// 
 			this.label5.AutoSize = true;
-			this.label5.Location = new System.Drawing.Point(685, 88);
+			this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label5.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label5.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label5.Location = new System.Drawing.Point(685, 110);
 			this.label5.Name = "label5";
 			this.label5.Size = new System.Drawing.Size(28, 13);
 			this.label5.TabIndex = 27;
@@ -200,7 +219,11 @@
 			// label8
 			// 
 			this.label8.AutoSize = true;
-			this.label8.Location = new System.Drawing.Point(788, 88);
+			this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label8.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label8.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label8.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label8.Location = new System.Drawing.Point(788, 110);
 			this.label8.Name = "label8";
 			this.label8.Size = new System.Drawing.Size(29, 13);
 			this.label8.TabIndex = 28;
@@ -209,7 +232,11 @@
 			// label9
 			// 
 			this.label9.AutoSize = true;
-			this.label9.Location = new System.Drawing.Point(685, 114);
+			this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label9.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label9.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label9.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label9.Location = new System.Drawing.Point(685, 136);
 			this.label9.Name = "label9";
 			this.label9.Size = new System.Drawing.Size(43, 13);
 			this.label9.TabIndex = 29;
@@ -218,7 +245,11 @@
 			// label10
 			// 
 			this.label10.AutoSize = true;
-			this.label10.Location = new System.Drawing.Point(788, 114);
+			this.label10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label10.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.label10.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label10.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label10.Location = new System.Drawing.Point(788, 136);
 			this.label10.Name = "label10";
 			this.label10.Size = new System.Drawing.Size(35, 13);
 			this.label10.TabIndex = 30;
@@ -226,7 +257,10 @@
 			// 
 			// valLeft
 			// 
-			this.valLeft.Location = new System.Drawing.Point(737, 86);
+			this.valLeft.BackColor = System.Drawing.Color.White;
+			this.valLeft.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valLeft.ForeColor = System.Drawing.Color.Black;
+			this.valLeft.Location = new System.Drawing.Point(737, 108);
 			this.valLeft.Maximum = new decimal(new int[] {
             10000,
             0,
@@ -244,7 +278,10 @@
 			// 
 			// valTop
 			// 
-			this.valTop.Location = new System.Drawing.Point(829, 86);
+			this.valTop.BackColor = System.Drawing.Color.White;
+			this.valTop.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valTop.ForeColor = System.Drawing.Color.Black;
+			this.valTop.Location = new System.Drawing.Point(829, 108);
 			this.valTop.Maximum = new decimal(new int[] {
             10000,
             0,
@@ -262,7 +299,10 @@
 			// 
 			// valBottom
 			// 
-			this.valBottom.Location = new System.Drawing.Point(737, 112);
+			this.valBottom.BackColor = System.Drawing.Color.White;
+			this.valBottom.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valBottom.ForeColor = System.Drawing.Color.Black;
+			this.valBottom.Location = new System.Drawing.Point(737, 134);
 			this.valBottom.Maximum = new decimal(new int[] {
             10000,
             0,
@@ -280,7 +320,10 @@
 			// 
 			// valRight
 			// 
-			this.valRight.Location = new System.Drawing.Point(829, 112);
+			this.valRight.BackColor = System.Drawing.Color.White;
+			this.valRight.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valRight.ForeColor = System.Drawing.Color.Black;
+			this.valRight.Location = new System.Drawing.Point(829, 134);
 			this.valRight.Maximum = new decimal(new int[] {
             10000,
             0,
@@ -299,7 +342,8 @@
 			// chkNoCrop
 			// 
 			this.chkNoCrop.AutoSize = true;
-			this.chkNoCrop.Location = new System.Drawing.Point(688, 138);
+			this.chkNoCrop.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkNoCrop.Location = new System.Drawing.Point(688, 160);
 			this.chkNoCrop.Name = "chkNoCrop";
 			this.chkNoCrop.Size = new System.Drawing.Size(84, 17);
 			this.chkNoCrop.TabIndex = 35;
@@ -309,22 +353,67 @@
 			// 
 			// cmdAdvanced
 			// 
-			this.cmdAdvanced.Location = new System.Drawing.Point(696, 461);
+			this.cmdAdvanced.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdAdvanced.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdAdvanced.Flat = false;
+			this.cmdAdvanced.Location = new System.Drawing.Point(687, 483);
 			this.cmdAdvanced.Name = "cmdAdvanced";
-			this.cmdAdvanced.Size = new System.Drawing.Size(189, 23);
+			this.cmdAdvanced.Size = new System.Drawing.Size(210, 23);
 			this.cmdAdvanced.TabIndex = 36;
-			this.cmdAdvanced.Text = "Adjust Part Transparencies...";
+			this.cmdAdvanced.Text = "Adjust Part Transparencies";
 			this.cmdAdvanced.UseVisualStyleBackColor = true;
 			this.cmdAdvanced.Click += new System.EventHandler(this.cmdAdvanced_Click);
 			// 
+			// previewPanel
+			// 
+			this.previewPanel.BackColor = System.Drawing.SystemColors.ActiveCaption;
+			this.previewPanel.Controls.Add(this.lblWait);
+			this.previewPanel.Location = new System.Drawing.Point(12, 33);
+			this.previewPanel.Name = "previewPanel";
+			this.previewPanel.PanelType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.previewPanel.Size = new System.Drawing.Size(666, 500);
+			this.previewPanel.TabIndex = 3;
+			this.previewPanel.TabSide = Desktop.Skinning.TabSide.None;
+			this.previewPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.previewPanel_Paint);
+			this.previewPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.previewPanel_MouseDown);
+			this.previewPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.previewPanel_MouseMove);
+			this.previewPanel.MouseUp += new System.Windows.Forms.MouseEventHandler(this.previewPanel_MouseUp);
+			// 
+			// lblWait
+			// 
+			this.lblWait.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
+			this.lblWait.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblWait.ForeColor = System.Drawing.SystemColors.ControlText;
+			this.lblWait.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblWait.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblWait.Location = new System.Drawing.Point(3, 237);
+			this.lblWait.Name = "lblWait";
+			this.lblWait.Size = new System.Drawing.Size(660, 13);
+			this.lblWait.TabIndex = 0;
+			this.lblWait.Text = "Please wait";
+			this.lblWait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 542);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(909, 30);
+			this.skinnedPanel1.TabIndex = 37;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
 			// ImageCropper
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(909, 552);
+			this.ClientSize = new System.Drawing.Size(909, 572);
 			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.cmdAdvanced);
 			this.Controls.Add(this.chkNoCrop);
 			this.Controls.Add(this.valRight);
@@ -341,19 +430,18 @@
 			this.Controls.Add(this.label6);
 			this.Controls.Add(this.label2);
 			this.Controls.Add(this.label1);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
 			this.Controls.Add(this.previewPanel);
 			this.Name = "ImageCropper";
 			this.Text = "Cropping Utility";
 			this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ImageCropper_FormClosed);
-			this.previewPanel.ResumeLayout(false);
 			this.panelManual.ResumeLayout(false);
 			this.panelManual.PerformLayout();
 			((System.ComponentModel.ISupportInitialize)(this.valLeft)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.valTop)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.valBottom)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.valRight)).EndInit();
+			this.previewPanel.ResumeLayout(false);
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -361,29 +449,30 @@
 
 		#endregion
 
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.Button cmdOK;
-		private Desktop.CommonControls.DBPanel previewPanel;
-		private System.Windows.Forms.Label label7;
-		private System.Windows.Forms.Label label6;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label lblWait;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedLabel label7;
+		private Desktop.Skinning.SkinnedLabel label6;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel lblWait;
 		private System.Windows.Forms.Timer tmrWait;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.Panel panelManual;
-		private System.Windows.Forms.Button cmdReimport;
-		private System.Windows.Forms.Label label4;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedPanel panelManual;
+		private Desktop.Skinning.SkinnedButton cmdReimport;
+		private Desktop.Skinning.SkinnedLabel label4;
 		private System.Windows.Forms.ToolTip toolTip1;
-		private System.Windows.Forms.Label label5;
-		private System.Windows.Forms.Label label8;
-		private System.Windows.Forms.Label label9;
-		private System.Windows.Forms.Label label10;
-		private System.Windows.Forms.NumericUpDown valLeft;
-		private System.Windows.Forms.NumericUpDown valTop;
-		private System.Windows.Forms.NumericUpDown valBottom;
-		private System.Windows.Forms.NumericUpDown valRight;
-		private System.Windows.Forms.CheckBox chkNoCrop;
-		private System.Windows.Forms.Button cmdAdvanced;
+		private Desktop.Skinning.SkinnedLabel label5;
+		private Desktop.Skinning.SkinnedLabel label8;
+		private Desktop.Skinning.SkinnedLabel label9;
+		private Desktop.Skinning.SkinnedLabel label10;
+		private Desktop.Skinning.SkinnedNumericUpDown valLeft;
+		private Desktop.Skinning.SkinnedNumericUpDown valTop;
+		private Desktop.Skinning.SkinnedNumericUpDown valBottom;
+		private Desktop.Skinning.SkinnedNumericUpDown valRight;
+		private Desktop.Skinning.SkinnedCheckBox chkNoCrop;
+		private Desktop.Skinning.SkinnedButton cmdAdvanced;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.CommonControls.DBPanel previewPanel;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/ImageCropper.cs b/editor source/SPNATI Character Editor/Forms/ImageCropper.cs
index 1e3372c97df137bc766f867415ddb77308751637..eeb46a063e5d7f1298ba9fbea3d6709890ac0f63 100644
--- a/editor source/SPNATI Character Editor/Forms/ImageCropper.cs	
+++ b/editor source/SPNATI Character Editor/Forms/ImageCropper.cs	
@@ -1,4 +1,5 @@
-using KisekaeImporter;
+using Desktop.Skinning;
+using KisekaeImporter;
 using KisekaeImporter.ImageImport;
 using System;
 using System.Collections.Generic;
@@ -7,7 +8,7 @@ using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class ImageCropper : Form
+	public partial class ImageCropper : SkinnedForm
 	{
 		private Image _previewImage;
 		private ImageImporter _importer = new ImageImporter();
@@ -84,7 +85,8 @@ namespace SPNATI_Character_Editor.Forms
 			_previewImage = image;
 			if (image == null)
 			{
-				MessageBox.Show("Failed to import. Is Kisekae running?");
+				FailedImport import = new FailedImport();
+				import.ShowDialog();
 				DialogResult = DialogResult.Cancel;
 				this.Close();
 			}
diff --git a/editor source/SPNATI Character Editor/Forms/ImageCropper.resx b/editor source/SPNATI Character Editor/Forms/ImageCropper.resx
index 40b74049de27d1df0d506af47a865010ce9aee2b..7c40808b1c4e75d2881b8b366c1bd675d635576b 100644
--- a/editor source/SPNATI Character Editor/Forms/ImageCropper.resx	
+++ b/editor source/SPNATI Character Editor/Forms/ImageCropper.resx	
@@ -129,7 +129,4 @@ Switch to Kisekae and flip all the images start at 1 to get them to layer proper
   <metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>113, 17</value>
   </metadata>
-  <metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>113, 17</value>
-  </metadata>
 </root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/KisekaeSetup.Designer.cs b/editor source/SPNATI Character Editor/Forms/KisekaeSetup.Designer.cs
index 5f0293a154fa0b5cb6228bfc0c2317a6755779bc..b2fc582426cfa2a421ef7480b927782047516f36 100644
--- a/editor source/SPNATI Character Editor/Forms/KisekaeSetup.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/KisekaeSetup.Designer.cs	
@@ -28,20 +28,25 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.cmdBrowseKisekae = new System.Windows.Forms.Button();
-			this.txtKisekae = new System.Windows.Forms.TextBox();
-			this.label5 = new System.Windows.Forms.Label();
-			this.cmdOk = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.label1 = new System.Windows.Forms.Label();
+			this.cmdBrowseKisekae = new Desktop.Skinning.SkinnedButton();
+			this.txtKisekae = new Desktop.Skinning.SkinnedTextBox();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdOk = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
 			this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
-			this.label2 = new System.Windows.Forms.Label();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// cmdBrowseKisekae
 			// 
 			this.cmdBrowseKisekae.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdBrowseKisekae.Location = new System.Drawing.Point(449, 52);
+			this.cmdBrowseKisekae.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdBrowseKisekae.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdBrowseKisekae.Flat = false;
+			this.cmdBrowseKisekae.Location = new System.Drawing.Point(449, 78);
 			this.cmdBrowseKisekae.Name = "cmdBrowseKisekae";
 			this.cmdBrowseKisekae.Size = new System.Drawing.Size(32, 23);
 			this.cmdBrowseKisekae.TabIndex = 11;
@@ -53,7 +58,10 @@
 			// 
 			this.txtKisekae.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.txtKisekae.Location = new System.Drawing.Point(109, 53);
+			this.txtKisekae.BackColor = System.Drawing.Color.White;
+			this.txtKisekae.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtKisekae.ForeColor = System.Drawing.Color.Black;
+			this.txtKisekae.Location = new System.Drawing.Point(109, 79);
 			this.txtKisekae.Name = "txtKisekae";
 			this.txtKisekae.Size = new System.Drawing.Size(334, 20);
 			this.txtKisekae.TabIndex = 10;
@@ -61,7 +69,8 @@
 			// label5
 			// 
 			this.label5.AutoSize = true;
-			this.label5.Location = new System.Drawing.Point(12, 56);
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label5.Location = new System.Drawing.Point(12, 82);
 			this.label5.Name = "label5";
 			this.label5.Size = new System.Drawing.Size(90, 13);
 			this.label5.TabIndex = 13;
@@ -70,7 +79,11 @@
 			// cmdOk
 			// 
 			this.cmdOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOk.Location = new System.Drawing.Point(325, 82);
+			this.cmdOk.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOk.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOk.Flat = false;
+			this.cmdOk.ForeColor = System.Drawing.Color.White;
+			this.cmdOk.Location = new System.Drawing.Point(334, 3);
 			this.cmdOk.Name = "cmdOk";
 			this.cmdOk.Size = new System.Drawing.Size(75, 23);
 			this.cmdOk.TabIndex = 12;
@@ -81,8 +94,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(406, 82);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(415, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 14;
@@ -93,7 +110,8 @@
 			// label1
 			// 
 			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.label1.Location = new System.Drawing.Point(12, 9);
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(12, 35);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(469, 41);
 			this.label1.TabIndex = 15;
@@ -107,30 +125,43 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(12, 88);
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.PrimaryDark;
+			this.label2.Location = new System.Drawing.Point(4, 8);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(201, 13);
 			this.label2.TabIndex = 16;
 			this.label2.Text = "You can change this later in File > Setup.";
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.label2);
+			this.skinnedPanel1.Controls.Add(this.cmdOk);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 111);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(493, 30);
+			this.skinnedPanel1.TabIndex = 17;
+			// 
 			// KisekaeSetup
 			// 
 			this.AcceptButton = this.cmdOk;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(493, 117);
+			this.ClientSize = new System.Drawing.Size(493, 141);
 			this.ControlBox = false;
-			this.Controls.Add(this.label2);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.label1);
-			this.Controls.Add(this.cmdCancel);
 			this.Controls.Add(this.cmdBrowseKisekae);
 			this.Controls.Add(this.txtKisekae);
 			this.Controls.Add(this.label5);
-			this.Controls.Add(this.cmdOk);
 			this.Name = "KisekaeSetup";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
 			this.Text = "Setup";
+			this.skinnedPanel1.ResumeLayout(false);
+			this.skinnedPanel1.PerformLayout();
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -138,13 +169,14 @@
 
 		#endregion
 
-		private System.Windows.Forms.Button cmdBrowseKisekae;
-		private System.Windows.Forms.TextBox txtKisekae;
-		private System.Windows.Forms.Label label5;
-		private System.Windows.Forms.Button cmdOk;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedButton cmdBrowseKisekae;
+		private Desktop.Skinning.SkinnedTextBox txtKisekae;
+		private Desktop.Skinning.SkinnedLabel label5;
+		private Desktop.Skinning.SkinnedButton cmdOk;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedLabel label1;
 		private System.Windows.Forms.OpenFileDialog openFileDialog1;
-		private System.Windows.Forms.Label label2;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/KisekaeSetup.cs b/editor source/SPNATI Character Editor/Forms/KisekaeSetup.cs
index 36c92e0e5d3a9dc9755005e25da1846f33df8b79..c0035efb86d00cb005605b8b0547cc93fc495c9d 100644
--- a/editor source/SPNATI Character Editor/Forms/KisekaeSetup.cs	
+++ b/editor source/SPNATI Character Editor/Forms/KisekaeSetup.cs	
@@ -1,10 +1,11 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.IO;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class KisekaeSetup : Form
+	public partial class KisekaeSetup : SkinnedForm
 	{
 		public KisekaeSetup()
 		{
diff --git a/editor source/SPNATI Character Editor/Forms/MacroManager.Designer.cs b/editor source/SPNATI Character Editor/Forms/MacroManager.Designer.cs
index aa9a13fdf44d02d9adf16dce8b401d9472cbd8ac..9d519f9b6119d85f81cd8dbe42c67349dd3421af 100644
--- a/editor source/SPNATI Character Editor/Forms/MacroManager.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/MacroManager.Designer.cs	
@@ -28,11 +28,13 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.lstMacros = new System.Windows.Forms.ListBox();
-			this.cmdDelete = new System.Windows.Forms.Button();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdEdit = new System.Windows.Forms.Button();
-			this.cmdNew = new System.Windows.Forms.Button();
+			this.lstMacros = new Desktop.Skinning.SkinnedListBox();
+			this.cmdDelete = new Desktop.Skinning.SkinnedButton();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdEdit = new Desktop.Skinning.SkinnedButton();
+			this.cmdNew = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// lstMacros
@@ -40,19 +42,25 @@
 			this.lstMacros.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstMacros.BackColor = System.Drawing.Color.White;
+			this.lstMacros.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstMacros.ForeColor = System.Drawing.Color.Black;
 			this.lstMacros.FormattingEnabled = true;
-			this.lstMacros.Location = new System.Drawing.Point(12, 12);
+			this.lstMacros.Location = new System.Drawing.Point(12, 33);
 			this.lstMacros.Name = "lstMacros";
-			this.lstMacros.Size = new System.Drawing.Size(178, 238);
+			this.lstMacros.Size = new System.Drawing.Size(169, 251);
 			this.lstMacros.TabIndex = 0;
 			this.lstMacros.DoubleClick += new System.EventHandler(this.lstMacros_DoubleClick);
 			// 
 			// cmdDelete
 			// 
 			this.cmdDelete.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdDelete.Location = new System.Drawing.Point(196, 70);
+			this.cmdDelete.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdDelete.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdDelete.Flat = false;
+			this.cmdDelete.Location = new System.Drawing.Point(187, 91);
 			this.cmdDelete.Name = "cmdDelete";
-			this.cmdDelete.Size = new System.Drawing.Size(57, 23);
+			this.cmdDelete.Size = new System.Drawing.Size(66, 23);
 			this.cmdDelete.TabIndex = 2;
 			this.cmdDelete.Text = "Delete";
 			this.cmdDelete.UseVisualStyleBackColor = true;
@@ -61,7 +69,11 @@
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(178, 255);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(187, 4);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 5;
@@ -72,9 +84,12 @@
 			// cmdEdit
 			// 
 			this.cmdEdit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdEdit.Location = new System.Drawing.Point(196, 12);
+			this.cmdEdit.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdEdit.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdEdit.Flat = false;
+			this.cmdEdit.Location = new System.Drawing.Point(187, 33);
 			this.cmdEdit.Name = "cmdEdit";
-			this.cmdEdit.Size = new System.Drawing.Size(57, 23);
+			this.cmdEdit.Size = new System.Drawing.Size(66, 23);
 			this.cmdEdit.TabIndex = 1;
 			this.cmdEdit.Text = "Edit";
 			this.cmdEdit.UseVisualStyleBackColor = true;
@@ -83,39 +98,55 @@
 			// cmdNew
 			// 
 			this.cmdNew.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdNew.Location = new System.Drawing.Point(196, 41);
+			this.cmdNew.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdNew.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdNew.Flat = false;
+			this.cmdNew.Location = new System.Drawing.Point(187, 62);
 			this.cmdNew.Name = "cmdNew";
-			this.cmdNew.Size = new System.Drawing.Size(57, 23);
+			this.cmdNew.Size = new System.Drawing.Size(66, 23);
 			this.cmdNew.TabIndex = 6;
 			this.cmdNew.Text = "New";
 			this.cmdNew.UseVisualStyleBackColor = true;
 			this.cmdNew.Click += new System.EventHandler(this.cmdNew_Click);
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 289);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(265, 30);
+			this.skinnedPanel1.TabIndex = 7;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
 			// MacroManager
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.ClientSize = new System.Drawing.Size(265, 290);
+			this.ClientSize = new System.Drawing.Size(265, 319);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.cmdNew);
 			this.Controls.Add(this.cmdEdit);
-			this.Controls.Add(this.cmdOK);
 			this.Controls.Add(this.cmdDelete);
 			this.Controls.Add(this.lstMacros);
 			this.Name = "MacroManager";
 			this.ShowIcon = false;
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Macro Manager";
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.ListBox lstMacros;
-		private System.Windows.Forms.Button cmdDelete;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdEdit;
-		private System.Windows.Forms.Button cmdNew;
+		private Desktop.Skinning.SkinnedListBox lstMacros;
+		private Desktop.Skinning.SkinnedButton cmdDelete;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdEdit;
+		private Desktop.Skinning.SkinnedButton cmdNew;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/MacroManager.cs b/editor source/SPNATI Character Editor/Forms/MacroManager.cs
index 617d691bcf90c19ef3405de661da6087150b3bc5..4f99250ef9db5b3f1483c466bbc41cdba249f46c 100644
--- a/editor source/SPNATI Character Editor/Forms/MacroManager.cs	
+++ b/editor source/SPNATI Character Editor/Forms/MacroManager.cs	
@@ -1,11 +1,14 @@
 using Desktop;
 using Desktop.Providers;
+using Desktop.Skinning;
 using System;
 using System.Windows.Forms;
+using Desktop.CommonControls;
+using SPNATI_Character_Editor.Controls;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class MacroManager : Form, IMacroEditor
+	public partial class MacroManager : SkinnedForm, IMacroEditor
 	{
 		private Type _macroType;
 		private string _key;
@@ -134,5 +137,10 @@ namespace SPNATI_Character_Editor.Forms
 		{
 			cmdEdit_Click(sender, e);
 		}
+
+		public void AddSpeedButtons(PropertyTable table)
+		{
+			CaseControl.AddSpeedButtons(table, "opponent_lost");
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Forms/MarkerSetup.Designer.cs b/editor source/SPNATI Character Editor/Forms/MarkerSetup.Designer.cs
index 79e19d31e75e799f9f1a13b5dd5f27ba1f2a4152..8fecd0eccbae64c23b3b758d2104ae18fc58b17b 100644
--- a/editor source/SPNATI Character Editor/Forms/MarkerSetup.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/MarkerSetup.Designer.cs	
@@ -28,16 +28,22 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
 			this.lstItems = new SPNATI_Character_Editor.Controls.SelectBox();
-			this.label1 = new System.Windows.Forms.Label();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(116, 226);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(125, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 1;
@@ -48,8 +54,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(197, 226);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(206, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 2;
@@ -57,26 +67,38 @@
 			this.cmdCancel.UseVisualStyleBackColor = true;
 			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
 			// 
+			// label1
+			// 
+			this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(12, 33);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(260, 40);
+			this.label1.TabIndex = 3;
+			this.label1.Text = "These markers will be considered \"set\" for the purposes of the preview.";
+			// 
 			// lstItems
 			// 
 			this.lstItems.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.lstItems.Location = new System.Drawing.Point(12, 52);
+			this.lstItems.Location = new System.Drawing.Point(12, 76);
 			this.lstItems.Name = "lstItems";
 			this.lstItems.SelectedItems = new string[0];
-			this.lstItems.Size = new System.Drawing.Size(260, 168);
+			this.lstItems.Size = new System.Drawing.Size(260, 149);
 			this.lstItems.TabIndex = 0;
 			// 
-			// label1
+			// skinnedPanel1
 			// 
-			this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-			this.label1.Location = new System.Drawing.Point(12, 9);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(260, 40);
-			this.label1.TabIndex = 3;
-			this.label1.Text = "These markers will be considered \"set\" for the purposes of the preview.";
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 231);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(284, 30);
+			this.skinnedPanel1.TabIndex = 4;
 			// 
 			// MarkerSetup
 			// 
@@ -86,13 +108,13 @@
 			this.CancelButton = this.cmdCancel;
 			this.ClientSize = new System.Drawing.Size(284, 261);
 			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.label1);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
 			this.Controls.Add(this.lstItems);
 			this.Name = "MarkerSetup";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Set Markers";
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 
 		}
@@ -100,8 +122,9 @@
 		#endregion
 
 		private Controls.SelectBox lstItems;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.Label label1;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/MarkerSetup.cs b/editor source/SPNATI Character Editor/Forms/MarkerSetup.cs
index a9523a54903642a0dfeb5d56ad21ef43ef3876e2..20ef4f3a6aab56ba7e2d35109430470749ca166f 100644
--- a/editor source/SPNATI Character Editor/Forms/MarkerSetup.cs	
+++ b/editor source/SPNATI Character Editor/Forms/MarkerSetup.cs	
@@ -1,10 +1,11 @@
-using System.Collections.Generic;
+using Desktop.Skinning;
+using System.Collections.Generic;
 using System.Linq;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class MarkerSetup : Form
+	public partial class MarkerSetup : SkinnedForm
 	{
 		public MarkerSetup()
 		{
diff --git a/editor source/SPNATI Character Editor/Forms/PoseExporter.Designer.cs b/editor source/SPNATI Character Editor/Forms/PoseExporter.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6a58840b1a722d5a59e2cd7163b8ed6c05533c3b
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/PoseExporter.Designer.cs	
@@ -0,0 +1,378 @@
+namespace SPNATI_Character_Editor.Forms
+{
+	partial class PoseExporter
+	{
+		/// <summary>
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary>
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Windows Form Designer generated code
+
+		/// <summary>
+		/// Required method for Designer support - do not modify
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.txtFile = new Desktop.Skinning.SkinnedTextBox();
+			this.cmdBrowse = new Desktop.Skinning.SkinnedButton();
+			this.cmdExport = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.lblName = new Desktop.Skinning.SkinnedLabel();
+			this.valWidth = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
+			this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
+			this.valHeight = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.label6 = new Desktop.Skinning.SkinnedLabel();
+			this.valFrameRate = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.preview = new SPNATI_Character_Editor.Controls.CharacterImageBox();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel2 = new Desktop.Skinning.SkinnedPanel();
+			((System.ComponentModel.ISupportInitialize)(this.valWidth)).BeginInit();
+			((System.ComponentModel.ISupportInitialize)(this.valHeight)).BeginInit();
+			((System.ComponentModel.ISupportInitialize)(this.valFrameRate)).BeginInit();
+			this.skinnedPanel1.SuspendLayout();
+			this.skinnedPanel2.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// label1
+			// 
+			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.White;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Primary;
+			this.label1.Location = new System.Drawing.Point(3, 6);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(507, 13);
+			this.label1.TabIndex = 0;
+			this.label1.Text = "This feature is for sharing poses on Discord and the like. Do NOT put these GIFs " +
+    "in your character\'s folder!";
+			// 
+			// label2
+			// 
+			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.White;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Primary;
+			this.label2.Location = new System.Drawing.Point(3, 22);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(379, 13);
+			this.label2.TabIndex = 1;
+			this.label2.Text = "The game does NOT need poses to be converted to GIFs in order to use them.";
+			// 
+			// txtFile
+			// 
+			this.txtFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtFile.BackColor = System.Drawing.Color.White;
+			this.txtFile.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtFile.ForeColor = System.Drawing.Color.Black;
+			this.txtFile.Location = new System.Drawing.Point(12, 71);
+			this.txtFile.Name = "txtFile";
+			this.txtFile.Size = new System.Drawing.Size(425, 20);
+			this.txtFile.TabIndex = 2;
+			// 
+			// cmdBrowse
+			// 
+			this.cmdBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdBrowse.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdBrowse.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdBrowse.Flat = false;
+			this.cmdBrowse.Location = new System.Drawing.Point(443, 69);
+			this.cmdBrowse.Name = "cmdBrowse";
+			this.cmdBrowse.Size = new System.Drawing.Size(80, 23);
+			this.cmdBrowse.TabIndex = 3;
+			this.cmdBrowse.Text = "Browse...";
+			this.cmdBrowse.UseVisualStyleBackColor = true;
+			this.cmdBrowse.Click += new System.EventHandler(this.cmdBrowse_Click);
+			// 
+			// cmdExport
+			// 
+			this.cmdExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdExport.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdExport.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdExport.Flat = false;
+			this.cmdExport.ForeColor = System.Drawing.Color.Red;
+			this.cmdExport.Location = new System.Drawing.Point(370, 3);
+			this.cmdExport.Name = "cmdExport";
+			this.cmdExport.Size = new System.Drawing.Size(75, 23);
+			this.cmdExport.TabIndex = 60;
+			this.cmdExport.Text = "Export";
+			this.cmdExport.UseVisualStyleBackColor = true;
+			this.cmdExport.Click += new System.EventHandler(this.cmdExport_Click);
+			// 
+			// cmdCancel
+			// 
+			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(452, 3);
+			this.cmdCancel.Name = "cmdCancel";
+			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
+			this.cmdCancel.TabIndex = 61;
+			this.cmdCancel.Text = "Cancel";
+			this.cmdCancel.UseVisualStyleBackColor = true;
+			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
+			// 
+			// label3
+			// 
+			this.label3.AutoSize = true;
+			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label3.Location = new System.Drawing.Point(15, 98);
+			this.label3.Name = "label3";
+			this.label3.Size = new System.Drawing.Size(81, 13);
+			this.label3.TabIndex = 6;
+			this.label3.Text = "Exporting Pose:";
+			// 
+			// lblName
+			// 
+			this.lblName.AutoSize = true;
+			this.lblName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lblName.ForeColor = System.Drawing.Color.Black;
+			this.lblName.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.lblName.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblName.Location = new System.Drawing.Point(102, 98);
+			this.lblName.Name = "lblName";
+			this.lblName.Size = new System.Drawing.Size(35, 13);
+			this.lblName.TabIndex = 7;
+			this.lblName.Text = "Name";
+			// 
+			// valWidth
+			// 
+			this.valWidth.BackColor = System.Drawing.Color.White;
+			this.valWidth.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valWidth.ForeColor = System.Drawing.Color.Black;
+			this.valWidth.Increment = new decimal(new int[] {
+            50,
+            0,
+            0,
+            0});
+			this.valWidth.Location = new System.Drawing.Point(59, 122);
+			this.valWidth.Maximum = new decimal(new int[] {
+            1400,
+            0,
+            0,
+            0});
+			this.valWidth.Minimum = new decimal(new int[] {
+            50,
+            0,
+            0,
+            0});
+			this.valWidth.Name = "valWidth";
+			this.valWidth.Size = new System.Drawing.Size(57, 20);
+			this.valWidth.TabIndex = 4;
+			this.valWidth.Value = new decimal(new int[] {
+            260,
+            0,
+            0,
+            0});
+			// 
+			// label4
+			// 
+			this.label4.AutoSize = true;
+			this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label4.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label4.Location = new System.Drawing.Point(15, 124);
+			this.label4.Name = "label4";
+			this.label4.Size = new System.Drawing.Size(38, 13);
+			this.label4.TabIndex = 9;
+			this.label4.Text = "Width:";
+			// 
+			// saveFileDialog1
+			// 
+			this.saveFileDialog1.Filter = "GIF files|*.gif";
+			// 
+			// label5
+			// 
+			this.label5.AutoSize = true;
+			this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label5.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label5.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label5.Location = new System.Drawing.Point(120, 124);
+			this.label5.Name = "label5";
+			this.label5.Size = new System.Drawing.Size(41, 13);
+			this.label5.TabIndex = 12;
+			this.label5.Text = "Height:";
+			// 
+			// valHeight
+			// 
+			this.valHeight.BackColor = System.Drawing.Color.White;
+			this.valHeight.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valHeight.ForeColor = System.Drawing.Color.Black;
+			this.valHeight.Increment = new decimal(new int[] {
+            50,
+            0,
+            0,
+            0});
+			this.valHeight.Location = new System.Drawing.Point(164, 122);
+			this.valHeight.Maximum = new decimal(new int[] {
+            1400,
+            0,
+            0,
+            0});
+			this.valHeight.Minimum = new decimal(new int[] {
+            50,
+            0,
+            0,
+            0});
+			this.valHeight.Name = "valHeight";
+			this.valHeight.Size = new System.Drawing.Size(57, 20);
+			this.valHeight.TabIndex = 5;
+			this.valHeight.Value = new decimal(new int[] {
+            600,
+            0,
+            0,
+            0});
+			// 
+			// label6
+			// 
+			this.label6.AutoSize = true;
+			this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label6.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label6.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label6.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label6.Location = new System.Drawing.Point(227, 124);
+			this.label6.Name = "label6";
+			this.label6.Size = new System.Drawing.Size(30, 13);
+			this.label6.TabIndex = 13;
+			this.label6.Text = "FPS:";
+			// 
+			// valFrameRate
+			// 
+			this.valFrameRate.BackColor = System.Drawing.Color.White;
+			this.valFrameRate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valFrameRate.ForeColor = System.Drawing.Color.Black;
+			this.valFrameRate.Location = new System.Drawing.Point(263, 122);
+			this.valFrameRate.Maximum = new decimal(new int[] {
+            60,
+            0,
+            0,
+            0});
+			this.valFrameRate.Minimum = new decimal(new int[] {
+            5,
+            0,
+            0,
+            0});
+			this.valFrameRate.Name = "valFrameRate";
+			this.valFrameRate.Size = new System.Drawing.Size(57, 20);
+			this.valFrameRate.TabIndex = 6;
+			this.valFrameRate.Value = new decimal(new int[] {
+            30,
+            0,
+            0,
+            0});
+			// 
+			// preview
+			// 
+			this.preview.Location = new System.Drawing.Point(326, 71);
+			this.preview.Name = "preview";
+			this.preview.ShowTextBox = false;
+			this.preview.Size = new System.Drawing.Size(197, 493);
+			this.preview.TabIndex = 10;
+			this.preview.Visible = false;
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdExport);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 148);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(530, 30);
+			this.skinnedPanel1.TabIndex = 62;
+			// 
+			// skinnedPanel2
+			// 
+			this.skinnedPanel2.Controls.Add(this.label1);
+			this.skinnedPanel2.Controls.Add(this.label2);
+			this.skinnedPanel2.Location = new System.Drawing.Point(1, 25);
+			this.skinnedPanel2.Name = "skinnedPanel2";
+			this.skinnedPanel2.PanelType = Desktop.Skinning.SkinnedBackgroundType.Primary;
+			this.skinnedPanel2.Size = new System.Drawing.Size(528, 40);
+			this.skinnedPanel2.TabIndex = 63;
+			// 
+			// PoseExporter
+			// 
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.ClientSize = new System.Drawing.Size(530, 178);
+			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel2);
+			this.Controls.Add(this.skinnedPanel1);
+			this.Controls.Add(this.valFrameRate);
+			this.Controls.Add(this.label6);
+			this.Controls.Add(this.label5);
+			this.Controls.Add(this.valHeight);
+			this.Controls.Add(this.label4);
+			this.Controls.Add(this.valWidth);
+			this.Controls.Add(this.lblName);
+			this.Controls.Add(this.label3);
+			this.Controls.Add(this.cmdBrowse);
+			this.Controls.Add(this.txtFile);
+			this.Controls.Add(this.preview);
+			this.Name = "PoseExporter";
+			this.ShowIcon = false;
+			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+			this.Text = "Export Pose as GIF";
+			((System.ComponentModel.ISupportInitialize)(this.valWidth)).EndInit();
+			((System.ComponentModel.ISupportInitialize)(this.valHeight)).EndInit();
+			((System.ComponentModel.ISupportInitialize)(this.valFrameRate)).EndInit();
+			this.skinnedPanel1.ResumeLayout(false);
+			this.skinnedPanel2.ResumeLayout(false);
+			this.skinnedPanel2.PerformLayout();
+			this.ResumeLayout(false);
+			this.PerformLayout();
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedTextBox txtFile;
+		private Desktop.Skinning.SkinnedButton cmdBrowse;
+		private Desktop.Skinning.SkinnedButton cmdExport;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedLabel lblName;
+		private Desktop.Skinning.SkinnedNumericUpDown valWidth;
+		private Desktop.Skinning.SkinnedLabel label4;
+		private Controls.CharacterImageBox preview;
+		private System.Windows.Forms.SaveFileDialog saveFileDialog1;
+		private Desktop.Skinning.SkinnedLabel label5;
+		private Desktop.Skinning.SkinnedNumericUpDown valHeight;
+		private Desktop.Skinning.SkinnedLabel label6;
+		private Desktop.Skinning.SkinnedNumericUpDown valFrameRate;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel2;
+	}
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/PoseExporter.cs b/editor source/SPNATI Character Editor/Forms/PoseExporter.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0ccf41d8c640d4b6144fad589980f28d065dad8f
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/PoseExporter.cs	
@@ -0,0 +1,100 @@
+using Desktop.Skinning;
+using System;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Forms
+{
+	public partial class PoseExporter : SkinnedForm
+	{
+		private Pose _pose;
+
+		public PoseExporter()
+		{
+			InitializeComponent();
+			preview.AutoPlayback = false;
+		}
+
+		public void SetPose(Pose pose)
+		{
+			_pose = pose;
+			lblName.Text = pose.Id;
+			txtFile.Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), pose.Id + ".gif");
+		}
+
+		private void cmdBrowse_Click(object sender, System.EventArgs e)
+		{
+			if (!string.IsNullOrEmpty(txtFile.Text))
+			{
+				saveFileDialog1.InitialDirectory = Path.GetDirectoryName(txtFile.Text);
+				saveFileDialog1.FileName = Path.GetFileName(txtFile.Text);
+			}
+			if (saveFileDialog1.ShowDialog() == DialogResult.OK)
+			{
+				txtFile.Text = saveFileDialog1.FileName;
+			}
+		}
+
+		private void cmdCancel_Click(object sender, EventArgs e)
+		{
+			Close();
+		}
+
+		private void cmdExport_Click(object sender, EventArgs e)
+		{
+			if (string.IsNullOrEmpty(txtFile.Text))
+			{
+				MessageBox.Show("Please specify a file name.");
+			}
+			preview.Width = (int)valWidth.Value;
+			preview.Height = (int)valHeight.Value;
+			preview.SetImage(new CharacterImage(_pose));
+
+			float duration = preview.Pose.Sprites.Max(s =>
+			{
+				float start = s.Start;
+				float end = s.Start;
+				if (s.Keyframes.Count > 0)
+				{
+					end = s.Keyframes[s.Keyframes.Count - 1].Time + start;
+				}
+				return end;
+			});
+
+			try
+			{
+				float fps = (float)valFrameRate.Value;
+				string file = txtFile.Text;
+				using (FileStream stream = new FileStream(file, FileMode.Create))
+				{
+					float delay = 1000.0f / fps;
+					delay /= 1000;
+					delay *= 100;
+					delay = (float)Math.Round(delay) * 10;
+					using (GifWriter writer = new GifWriter(stream, (int)delay))
+					{
+						for (float time = 0; ; time += (1 / fps))
+						{
+							preview.SetTime(time);
+							Image img = preview.GetImage();
+							writer.WriteFrame(img);
+							img.Dispose();
+							if (time >= duration)
+							{
+								break;
+							}
+						}
+					}
+					MessageBox.Show($"Successfully exported to {file}.");
+					Close();
+				}
+			}
+			catch (Exception ex)
+			{
+				MessageBox.Show(ex.ToString());
+			}
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Forms/PoseExporter.resx b/editor source/SPNATI Character Editor/Forms/PoseExporter.resx
new file mode 100644
index 0000000000000000000000000000000000000000..053da59c9c8298e1f9384cb6f3de991b9e5df837
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/PoseExporter.resx	
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="saveFileDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/PoseSettingsForm.Designer.cs b/editor source/SPNATI Character Editor/Forms/PoseSettingsForm.Designer.cs
index eb4daa744b2bfd7f75d4308fcc2222bb30262efd..0f2006e61dad295d2e3d6ff9033edd937c7df6e2 100644
--- a/editor source/SPNATI Character Editor/Forms/PoseSettingsForm.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/PoseSettingsForm.Designer.cs	
@@ -29,14 +29,16 @@
 		private void InitializeComponent()
 		{
 			this.panelHead = new System.Windows.Forms.FlowLayoutPanel();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.label1 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
 			this.panelBody = new System.Windows.Forms.FlowLayoutPanel();
-			this.label3 = new System.Windows.Forms.Label();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
 			this.panelClothing = new System.Windows.Forms.FlowLayoutPanel();
-			this.chkManual = new System.Windows.Forms.CheckBox();
+			this.chkManual = new Desktop.Skinning.SkinnedCheckBox();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// panelHead
@@ -44,7 +46,7 @@
 			this.panelHead.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.panelHead.AutoScroll = true;
-			this.panelHead.Location = new System.Drawing.Point(12, 47);
+			this.panelHead.Location = new System.Drawing.Point(12, 71);
 			this.panelHead.Name = "panelHead";
 			this.panelHead.Size = new System.Drawing.Size(634, 154);
 			this.panelHead.TabIndex = 0;
@@ -52,7 +54,11 @@
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(490, 623);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(499, 4);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 1;
@@ -63,8 +69,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(571, 623);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(580, 4);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 2;
@@ -76,7 +86,8 @@
 			// 
 			this.label1.AutoSize = true;
 			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.label1.Location = new System.Drawing.Point(9, 31);
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.label1.Location = new System.Drawing.Point(9, 55);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(37, 13);
 			this.label1.TabIndex = 3;
@@ -86,7 +97,8 @@
 			// 
 			this.label2.AutoSize = true;
 			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.label2.Location = new System.Drawing.Point(9, 204);
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.label2.Location = new System.Drawing.Point(9, 228);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(35, 13);
 			this.label2.TabIndex = 5;
@@ -97,7 +109,7 @@
 			this.panelBody.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.panelBody.AutoScroll = true;
-			this.panelBody.Location = new System.Drawing.Point(12, 220);
+			this.panelBody.Location = new System.Drawing.Point(12, 244);
 			this.panelBody.Name = "panelBody";
 			this.panelBody.Size = new System.Drawing.Size(634, 154);
 			this.panelBody.TabIndex = 4;
@@ -106,7 +118,8 @@
 			// 
 			this.label3.AutoSize = true;
 			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.label3.Location = new System.Drawing.Point(9, 377);
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Heading;
+			this.label3.Location = new System.Drawing.Point(9, 401);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(53, 13);
 			this.label3.TabIndex = 7;
@@ -117,7 +130,7 @@
 			this.panelClothing.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.panelClothing.AutoScroll = true;
-			this.panelClothing.Location = new System.Drawing.Point(12, 393);
+			this.panelClothing.Location = new System.Drawing.Point(12, 417);
 			this.panelClothing.Name = "panelClothing";
 			this.panelClothing.Size = new System.Drawing.Size(634, 224);
 			this.panelClothing.TabIndex = 6;
@@ -125,7 +138,7 @@
 			// chkManual
 			// 
 			this.chkManual.AutoSize = true;
-			this.chkManual.Location = new System.Drawing.Point(12, 5);
+			this.chkManual.Location = new System.Drawing.Point(12, 32);
 			this.chkManual.Name = "chkManual";
 			this.chkManual.Size = new System.Drawing.Size(485, 17);
 			this.chkManual.TabIndex = 8;
@@ -133,26 +146,37 @@
     "ng character, etc.)";
 			this.chkManual.UseVisualStyleBackColor = true;
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 651);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(658, 30);
+			this.skinnedPanel1.TabIndex = 9;
+			// 
 			// PoseSettingsForm
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(658, 658);
+			this.ClientSize = new System.Drawing.Size(658, 681);
 			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.chkManual);
 			this.Controls.Add(this.label3);
 			this.Controls.Add(this.panelClothing);
 			this.Controls.Add(this.label2);
 			this.Controls.Add(this.panelBody);
 			this.Controls.Add(this.label1);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
 			this.Controls.Add(this.panelHead);
 			this.Name = "PoseSettingsForm";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Pose Settings";
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -161,13 +185,14 @@
 		#endregion
 
 		private System.Windows.Forms.FlowLayoutPanel panelHead;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label label2;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel label2;
 		private System.Windows.Forms.FlowLayoutPanel panelBody;
-		private System.Windows.Forms.Label label3;
+		private Desktop.Skinning.SkinnedLabel label3;
 		private System.Windows.Forms.FlowLayoutPanel panelClothing;
-		private System.Windows.Forms.CheckBox chkManual;
+		private Desktop.Skinning.SkinnedCheckBox chkManual;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/PoseSettingsForm.cs b/editor source/SPNATI Character Editor/Forms/PoseSettingsForm.cs
index 8688f21747368cd076c074eef83ded1936a9f2b4..8429a4b9c83c25525154f4c3978c4f204ee208bc 100644
--- a/editor source/SPNATI Character Editor/Forms/PoseSettingsForm.cs	
+++ b/editor source/SPNATI Character Editor/Forms/PoseSettingsForm.cs	
@@ -1,4 +1,5 @@
-using KisekaeImporter;
+using Desktop.Skinning;
+using KisekaeImporter;
 using SPNATI_Character_Editor.Controls;
 using System.Collections.Generic;
 using System.Globalization;
@@ -6,7 +7,7 @@ using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class PoseSettingsForm : Form
+	public partial class PoseSettingsForm : SkinnedForm
 	{
 		private Dictionary<KisekaePart, PartTransparencySlider> _sliders = new Dictionary<KisekaePart, PartTransparencySlider>();
 
diff --git a/editor source/SPNATI Character Editor/Forms/PropImporter.Designer.cs b/editor source/SPNATI Character Editor/Forms/PropImporter.Designer.cs
index 5e2d1a17caa7e7e495aeac2aeb6bd83950b42af9..15f000cdb067e6ec7f70db2f6b3774027bbca760 100644
--- a/editor source/SPNATI Character Editor/Forms/PropImporter.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/PropImporter.Designer.cs	
@@ -28,29 +28,38 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.gridMissingImages = new System.Windows.Forms.DataGridView();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.gridMissingImages = new Desktop.Skinning.SkinnedDataGridView();
 			this.ColImageName = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.ColImage = new System.Windows.Forms.DataGridViewImageColumn();
 			this.ColAssign = new System.Windows.Forms.DataGridViewButtonColumn();
-			this.label1 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
-			this.label3 = new System.Windows.Forms.Label();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
 			this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
-			this.lblReady = new System.Windows.Forms.Label();
-			this.cmdCancel = new System.Windows.Forms.Button();
+			this.lblReady = new Desktop.Skinning.SkinnedLabel();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.dataGridViewButtonColumn1 = new System.Windows.Forms.DataGridViewButtonColumn();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
 			((System.ComponentModel.ISupportInitialize)(this.gridMissingImages)).BeginInit();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdOK.Enabled = false;
-			this.cmdOK.Location = new System.Drawing.Point(187, 533);
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.Location = new System.Drawing.Point(196, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 1;
-			this.cmdOK.Text = "OK";
+			this.cmdOK.Text = "Import";
 			this.cmdOK.UseVisualStyleBackColor = true;
 			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
 			// 
@@ -63,17 +72,49 @@
 			this.gridMissingImages.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.gridMissingImages.BackgroundColor = System.Drawing.Color.White;
+			this.gridMissingImages.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.gridMissingImages.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridMissingImages.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
 			this.gridMissingImages.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.gridMissingImages.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.ColImageName,
             this.ColImage,
             this.ColAssign});
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle2.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.gridMissingImages.DefaultCellStyle = dataGridViewCellStyle2;
 			this.gridMissingImages.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
-			this.gridMissingImages.Location = new System.Drawing.Point(12, 76);
+			this.gridMissingImages.EnableHeadersVisualStyles = false;
+			this.gridMissingImages.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.gridMissingImages.GridColor = System.Drawing.Color.FromArgb(((int)(((byte)(142)))), ((int)(((byte)(153)))), ((int)(((byte)(243)))));
+			this.gridMissingImages.Location = new System.Drawing.Point(12, 106);
 			this.gridMissingImages.Name = "gridMissingImages";
+			this.gridMissingImages.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle3.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.gridMissingImages.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
 			this.gridMissingImages.RowHeadersVisible = false;
 			this.gridMissingImages.RowTemplate.Height = 100;
-			this.gridMissingImages.Size = new System.Drawing.Size(331, 406);
+			this.gridMissingImages.Size = new System.Drawing.Size(331, 407);
 			this.gridMissingImages.TabIndex = 0;
 			this.gridMissingImages.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridMissingImages_CellContentClick);
 			// 
@@ -99,10 +140,13 @@
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.label1.Location = new System.Drawing.Point(12, 9);
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.Black;
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(12, 36);
 			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(154, 20);
+			this.label1.Size = new System.Drawing.Size(106, 13);
 			this.label1.TabIndex = 2;
 			this.label1.Text = "We Need Your Help!";
 			// 
@@ -110,7 +154,11 @@
 			// 
 			this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.label2.Location = new System.Drawing.Point(9, 29);
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.Black;
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label2.Location = new System.Drawing.Point(9, 59);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(334, 44);
 			this.label2.TabIndex = 3;
@@ -122,7 +170,11 @@
 			// 
 			this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.label3.Location = new System.Drawing.Point(12, 485);
+			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label3.ForeColor = System.Drawing.Color.Black;
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label3.Location = new System.Drawing.Point(12, 516);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(331, 45);
 			this.label3.TabIndex = 4;
@@ -138,9 +190,11 @@
 			// 
 			this.lblReady.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblReady.Font = new System.Drawing.Font("Microsoft Sans Serif", 30F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.lblReady.Font = new System.Drawing.Font("Segoe UI", 28F);
 			this.lblReady.ForeColor = System.Drawing.Color.Green;
-			this.lblReady.Location = new System.Drawing.Point(12, 9);
+			this.lblReady.Highlight = Desktop.Skinning.SkinnedHighlight.Good;
+			this.lblReady.Level = Desktop.Skinning.SkinnedLabelLevel.Finished;
+			this.lblReady.Location = new System.Drawing.Point(12, 38);
 			this.lblReady.Name = "lblReady";
 			this.lblReady.Size = new System.Drawing.Size(331, 64);
 			this.lblReady.TabIndex = 5;
@@ -151,8 +205,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(268, 533);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(277, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 6;
@@ -160,27 +218,43 @@
 			this.cmdCancel.UseVisualStyleBackColor = true;
 			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
 			// 
+			// dataGridViewButtonColumn1
+			// 
+			this.dataGridViewButtonColumn1.HeaderText = "Source";
+			this.dataGridViewButtonColumn1.Name = "dataGridViewButtonColumn1";
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 569);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(355, 30);
+			this.skinnedPanel1.TabIndex = 7;
+			// 
 			// PropImporter
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(355, 568);
+			this.ClientSize = new System.Drawing.Size(355, 599);
 			this.ControlBox = false;
-			this.Controls.Add(this.cmdCancel);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.lblReady);
 			this.Controls.Add(this.label3);
 			this.Controls.Add(this.label2);
 			this.Controls.Add(this.label1);
 			this.Controls.Add(this.gridMissingImages);
-			this.Controls.Add(this.cmdOK);
 			this.Name = "PropImporter";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Import Images";
 			this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.PropImporter_FormClosing);
 			this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.PropImporter_FormClosed);
 			((System.ComponentModel.ISupportInitialize)(this.gridMissingImages)).EndInit();
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -188,16 +262,18 @@
 
 		#endregion
 
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.DataGridView gridMissingImages;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.Label label3;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedDataGridView gridMissingImages;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedLabel label3;
 		private System.Windows.Forms.OpenFileDialog openFileDialog1;
 		private System.Windows.Forms.DataGridViewTextBoxColumn ColImageName;
 		private System.Windows.Forms.DataGridViewImageColumn ColImage;
 		private System.Windows.Forms.DataGridViewButtonColumn ColAssign;
-		private System.Windows.Forms.Label lblReady;
-		private System.Windows.Forms.Button cmdCancel;
+		private Desktop.Skinning.SkinnedLabel lblReady;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private System.Windows.Forms.DataGridViewButtonColumn dataGridViewButtonColumn1;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/PropImporter.cs b/editor source/SPNATI Character Editor/Forms/PropImporter.cs
index 90cabc22ebf32909bd2d8571f21422b1f093e541..6f2013ba57eabad5a5a5d5144abe97af265f1a9f 100644
--- a/editor source/SPNATI Character Editor/Forms/PropImporter.cs	
+++ b/editor source/SPNATI Character Editor/Forms/PropImporter.cs	
@@ -1,11 +1,12 @@
-using System.Collections.Generic;
+using Desktop.Skinning;
+using System.Collections.Generic;
 using System.Drawing;
 using System.IO;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor
 {
-	public partial class PropImporter : Form
+	public partial class PropImporter : SkinnedForm
 	{
 		private string _imagesDirectory;
 
diff --git a/editor source/SPNATI Character Editor/Forms/PropImporter.resx b/editor source/SPNATI Character Editor/Forms/PropImporter.resx
index 3267c82f82988eed125ae83287d3353eb3941587..668ce6a798bacf6dbea8dd0edeeea1a8b3aa5963 100644
--- a/editor source/SPNATI Character Editor/Forms/PropImporter.resx	
+++ b/editor source/SPNATI Character Editor/Forms/PropImporter.resx	
@@ -126,15 +126,6 @@
   <metadata name="ColAssign.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
-  <metadata name="ColImageName.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>True</value>
-  </metadata>
-  <metadata name="ColImage.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>True</value>
-  </metadata>
-  <metadata name="ColAssign.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>True</value>
-  </metadata>
   <metadata name="openFileDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>17, 17</value>
   </metadata>
diff --git a/editor source/SPNATI Character Editor/Forms/ResponseSetupForm.Designer.cs b/editor source/SPNATI Character Editor/Forms/ResponseSetupForm.Designer.cs
index f7f3711f9ffb43f5b849021752995f4a6f809942..7701491d06a981a2a31b703db336ad85050ed0d2 100644
--- a/editor source/SPNATI Character Editor/Forms/ResponseSetupForm.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/ResponseSetupForm.Designer.cs	
@@ -28,19 +28,22 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.lblMarker = new System.Windows.Forms.Label();
-			this.txtMarker = new System.Windows.Forms.TextBox();
-			this.lstStages = new System.Windows.Forms.ListView();
-			this.chkOneTime = new System.Windows.Forms.CheckBox();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.lblMarker = new Desktop.Skinning.SkinnedLabel();
+			this.txtMarker = new Desktop.Skinning.SkinnedTextBox();
+			this.lstStages = new Desktop.Skinning.SkinnedListView();
+			this.chkOneTime = new Desktop.Skinning.SkinnedCheckBox();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(12, 9);
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label1.Location = new System.Drawing.Point(12, 33);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(163, 13);
 			this.label1.TabIndex = 1;
@@ -50,7 +53,8 @@
 			// 
 			this.lblMarker.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
 			this.lblMarker.AutoSize = true;
-			this.lblMarker.Location = new System.Drawing.Point(150, 165);
+			this.lblMarker.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblMarker.Location = new System.Drawing.Point(150, 189);
 			this.lblMarker.Name = "lblMarker";
 			this.lblMarker.Size = new System.Drawing.Size(43, 13);
 			this.lblMarker.TabIndex = 2;
@@ -59,8 +63,11 @@
 			// txtMarker
 			// 
 			this.txtMarker.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.txtMarker.BackColor = System.Drawing.Color.White;
 			this.txtMarker.Enabled = false;
-			this.txtMarker.Location = new System.Drawing.Point(199, 162);
+			this.txtMarker.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtMarker.ForeColor = System.Drawing.Color.Black;
+			this.txtMarker.Location = new System.Drawing.Point(199, 186);
 			this.txtMarker.Name = "txtMarker";
 			this.txtMarker.Size = new System.Drawing.Size(73, 20);
 			this.txtMarker.TabIndex = 3;
@@ -68,9 +75,10 @@
 			// lstStages
 			// 
 			this.lstStages.CheckBoxes = true;
-			this.lstStages.Location = new System.Drawing.Point(12, 25);
+			this.lstStages.Location = new System.Drawing.Point(12, 49);
 			this.lstStages.MultiSelect = false;
 			this.lstStages.Name = "lstStages";
+			this.lstStages.OwnerDraw = true;
 			this.lstStages.Size = new System.Drawing.Size(260, 132);
 			this.lstStages.TabIndex = 4;
 			this.lstStages.UseCompatibleStateImageBehavior = false;
@@ -81,7 +89,7 @@
 			// 
 			this.chkOneTime.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
 			this.chkOneTime.AutoSize = true;
-			this.chkOneTime.Location = new System.Drawing.Point(12, 164);
+			this.chkOneTime.Location = new System.Drawing.Point(12, 188);
 			this.chkOneTime.Name = "chkOneTime";
 			this.chkOneTime.Size = new System.Drawing.Size(123, 17);
 			this.chkOneTime.TabIndex = 5;
@@ -92,7 +100,11 @@
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(116, 188);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(125, 4);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 6;
@@ -103,8 +115,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(197, 188);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(206, 4);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 7;
@@ -112,16 +128,26 @@
 			this.cmdCancel.UseVisualStyleBackColor = true;
 			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 217);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(284, 30);
+			this.skinnedPanel1.TabIndex = 8;
+			// 
 			// ResponseSetupForm
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(284, 223);
+			this.ClientSize = new System.Drawing.Size(284, 247);
 			this.ControlBox = false;
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.chkOneTime);
 			this.Controls.Add(this.lstStages);
 			this.Controls.Add(this.txtMarker);
@@ -130,6 +156,7 @@
 			this.Name = "ResponseSetupForm";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Setup Response";
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -137,12 +164,13 @@
 
 		#endregion
 
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label lblMarker;
-		private System.Windows.Forms.TextBox txtMarker;
-		private System.Windows.Forms.ListView lstStages;
-		private System.Windows.Forms.CheckBox chkOneTime;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel lblMarker;
+		private Desktop.Skinning.SkinnedTextBox txtMarker;
+		private Desktop.Skinning.SkinnedListView lstStages;
+		private Desktop.Skinning.SkinnedCheckBox chkOneTime;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/ResponseSetupForm.cs b/editor source/SPNATI Character Editor/Forms/ResponseSetupForm.cs
index b72c7f43ddcadc9d8c6054f14a00493192842511..b26e14aafd7111e3c1716fb5a5fe888bbc7937b1 100644
--- a/editor source/SPNATI Character Editor/Forms/ResponseSetupForm.cs	
+++ b/editor source/SPNATI Character Editor/Forms/ResponseSetupForm.cs	
@@ -1,10 +1,11 @@
-using System.Collections.Generic;
+using Desktop.Skinning;
+using System.Collections.Generic;
 using System.Drawing;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class ResponseSetupForm : Form
+	public partial class ResponseSetupForm : SkinnedForm
 	{
 		private Character _character;
 		private Case _original;
diff --git a/editor source/SPNATI Character Editor/Forms/StageImageSelection.Designer.cs b/editor source/SPNATI Character Editor/Forms/StageImageSelection.Designer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a614d6f35ffd69643868f4c0f385ea1673a3ed32
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/StageImageSelection.Designer.cs	
@@ -0,0 +1,198 @@
+namespace SPNATI_Character_Editor.Forms
+{
+	partial class StageImageSelection
+	{
+		/// <summary>
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary>
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Windows Form Designer generated code
+
+		/// <summary>
+		/// Required method for Designer support - do not modify
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+			this.grid = new Desktop.Skinning.SkinnedDataGridView();
+			this.ColStage = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.ColImage = new Desktop.Skinning.SkinnedDataGridViewComboBoxColumn();
+			this.ColClear = new Desktop.Skinning.SkinnedDataGridViewButtonColumn();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			((System.ComponentModel.ISupportInitialize)(this.grid)).BeginInit();
+			this.skinnedPanel1.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// grid
+			// 
+			this.grid.AllowUserToAddRows = false;
+			this.grid.AllowUserToDeleteRows = false;
+			this.grid.AllowUserToResizeColumns = false;
+			this.grid.AllowUserToResizeRows = false;
+			this.grid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.grid.BackgroundColor = System.Drawing.Color.White;
+			this.grid.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.grid.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 5);
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.grid.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
+			this.grid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+			this.grid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.ColStage,
+            this.ColImage,
+            this.ColClear});
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle2.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
+			dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.grid.DefaultCellStyle = dataGridViewCellStyle2;
+			this.grid.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
+			this.grid.EnableHeadersVisualStyles = false;
+			this.grid.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.grid.GridColor = System.Drawing.Color.FromArgb(((int)(((byte)(142)))), ((int)(((byte)(153)))), ((int)(((byte)(243)))));
+			this.grid.Location = new System.Drawing.Point(12, 37);
+			this.grid.Name = "grid";
+			this.grid.RowHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
+			dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle3.BackColor = System.Drawing.Color.White;
+			dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			dataGridViewCellStyle3.ForeColor = System.Drawing.Color.Black;
+			dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.grid.RowHeadersDefaultCellStyle = dataGridViewCellStyle3;
+			this.grid.RowHeadersVisible = false;
+			this.grid.RowHeadersWidth = 21;
+			this.grid.Size = new System.Drawing.Size(260, 290);
+			this.grid.TabIndex = 1;
+			this.grid.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.grid_CellContentClick);
+			this.grid.CellEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.grid_CellEnter);
+			this.grid.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.grid_CellPainting);
+			this.grid.CellParsing += new System.Windows.Forms.DataGridViewCellParsingEventHandler(this.grid_CellParsing);
+			this.grid.DataError += new System.Windows.Forms.DataGridViewDataErrorEventHandler(this.grid_DataError);
+			this.grid.EditingControlShowing += new System.Windows.Forms.DataGridViewEditingControlShowingEventHandler(this.grid_EditingControlShowing);
+			// 
+			// ColStage
+			// 
+			this.ColStage.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			this.ColStage.FillWeight = 98.47716F;
+			this.ColStage.HeaderText = "Stage";
+			this.ColStage.Name = "ColStage";
+			this.ColStage.ReadOnly = true;
+			// 
+			// ColImage
+			// 
+			this.ColImage.FillWeight = 101.5228F;
+			this.ColImage.HeaderText = "Image";
+			this.ColImage.Name = "ColImage";
+			this.ColImage.Resizable = System.Windows.Forms.DataGridViewTriState.True;
+			this.ColImage.Width = 120;
+			// 
+			// ColClear
+			// 
+			this.ColClear.HeaderText = "";
+			this.ColClear.Name = "ColClear";
+			this.ColClear.Width = 21;
+			// 
+			// cmdOK
+			// 
+			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(125, 4);
+			this.cmdOK.Name = "cmdOK";
+			this.cmdOK.Size = new System.Drawing.Size(75, 23);
+			this.cmdOK.TabIndex = 2;
+			this.cmdOK.Text = "OK";
+			this.cmdOK.UseVisualStyleBackColor = true;
+			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
+			// 
+			// cmdCancel
+			// 
+			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(206, 4);
+			this.cmdCancel.Name = "cmdCancel";
+			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
+			this.cmdCancel.TabIndex = 3;
+			this.cmdCancel.Text = "Cancel";
+			this.cmdCancel.UseVisualStyleBackColor = true;
+			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 338);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(284, 30);
+			this.skinnedPanel1.TabIndex = 4;
+			// 
+			// StageImageSelection
+			// 
+			this.AcceptButton = this.cmdOK;
+			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.CancelButton = this.cmdCancel;
+			this.ClientSize = new System.Drawing.Size(284, 368);
+			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
+			this.Controls.Add(this.grid);
+			this.Name = "StageImageSelection";
+			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+			this.Text = "Select Images Per Stage";
+			((System.ComponentModel.ISupportInitialize)(this.grid)).EndInit();
+			this.skinnedPanel1.ResumeLayout(false);
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private Desktop.Skinning.SkinnedDataGridView grid;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private System.Windows.Forms.DataGridViewTextBoxColumn ColStage;
+		private Desktop.Skinning.SkinnedDataGridViewComboBoxColumn ColImage;
+		private Desktop.Skinning.SkinnedDataGridViewButtonColumn ColClear;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+	}
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/StageImageSelection.cs b/editor source/SPNATI Character Editor/Forms/StageImageSelection.cs
new file mode 100644
index 0000000000000000000000000000000000000000..03d3757c55eacb3060b64f41b6b02f397543124f
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/StageImageSelection.cs	
@@ -0,0 +1,233 @@
+using Desktop.Skinning;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Forms
+{
+	public partial class StageImageSelection : SkinnedForm
+	{
+		private DialogueLine _line;
+		private Character _character;
+		private ImageLibrary _imageLibrary;
+
+		public StageImageSelection()
+		{
+			InitializeComponent();
+			ColClear.Flat = true;
+		}
+		public StageImageSelection(Character character, DialogueLine line, Case workingCase) : this()
+		{
+			_line = line;
+			_character = character;
+			_imageLibrary = ImageLibrary.Get(_character);
+
+			foreach (int stage in workingCase.Stages)
+			{
+				DataGridViewRow row = grid.Rows[grid.Rows.Add()];
+				row.Tag = stage;
+				row.Cells[nameof(ColStage)].Value = _character.LayerToStageName(stage);
+
+				UpdateAvailableImages(stage);
+
+				if (_line.StageImages.ContainsKey(stage))
+				{
+					LineImage lineImage = _line.StageImages[stage];
+					string imageKey = lineImage.Image;
+					if (!lineImage.IsGenericImage)
+					{
+						SkinnedDataGridViewComboBoxCell cell = row.Cells[nameof(ColImage)] as SkinnedDataGridViewComboBoxCell;
+						imageKey = DialogueLine.GetStageImage(stage, imageKey);
+						SetImage(cell, imageKey);
+					}
+				}
+			}
+		}
+
+		private void UpdateAvailableImages(int stageId)
+		{
+			DataGridViewRow row = null;
+			foreach (DataGridViewRow r in grid.Rows)
+			{
+				if ((int)r.Tag == stageId)
+				{
+					row = r;
+					break;
+				}
+			}
+			if (row == null) { return; }
+			SkinnedDataGridViewComboBoxCell cell = row.Cells[ColImage.Index] as SkinnedDataGridViewComboBoxCell;
+			cell.ValueType = typeof(CharacterImage);
+			cell.Items.Clear();
+			List<CharacterImage> images = new List<CharacterImage>();
+			images.AddRange(_imageLibrary.GetImages(stageId));
+			if (Config.UsePrefixlessImages)
+			{
+				foreach (CharacterImage img in _imageLibrary.GetImages(-1))
+				{
+					string file = img.Name;
+					if (!_imageLibrary.FilterImage(_character, file))
+					{
+						images.Add(img);
+					}
+				}
+			}
+			foreach (CharacterImage img in images)
+			{
+				cell.Items.Add(img);
+			}
+		}
+
+		private void SetImage(SkinnedDataGridViewComboBoxCell cell, string key)
+		{
+			string defaultKey = DialogueLine.GetDefaultImage(key);
+			foreach (object item in cell.Items)
+			{
+				CharacterImage image = item as CharacterImage;
+				if (image != null && image.DefaultName == defaultKey)
+				{
+					cell.Value = image;
+					return;
+				}
+			}
+		}
+
+		private void grid_CellEnter(object sender, DataGridViewCellEventArgs e)
+		{
+			if (e.RowIndex == -1)
+				return;
+			DataGridViewRow row = grid.Rows[e.RowIndex];
+			string image = row.Cells[nameof(ColImage)].Value?.ToString();
+			ShowImage(image, (int)row.Tag);
+		}
+
+		private void ShowImage(string image, int stage)
+		{
+			CharacterImage img = null;
+			img = _imageLibrary.Find(image);
+			if (img == null)
+			{
+				image = DialogueLine.GetStageImage(stage, DialogueLine.GetDefaultImage(image));
+				img = _imageLibrary.Find(image);
+			}
+			if (img != null)
+			{
+				Desktop.IWorkspace ws = Desktop.Shell.Instance.GetWorkspace(_character);
+				if (ws != null)
+				{
+					ws.SendMessage(WorkspaceMessages.UpdatePreviewImage, img);
+				}
+			}
+		}
+
+		private void cmdOK_Click(object sender, EventArgs e)
+		{
+			_line.StageImages.Clear();
+			foreach (DataGridViewRow row in grid.Rows)
+			{
+				CharacterImage img = row.Cells[ColImage.Index].Value as CharacterImage;
+				if (img != null)
+				{
+					string name = img.Name;
+					if (!img.IsGeneric)
+					{
+						name = DialogueLine.GetDefaultImage(name);
+					}
+					if (name == _line.Image)
+					{
+						continue;
+					}
+					LineImage li = new LineImage(img.Name, img.IsGeneric);
+					_line.StageImages[(int)row.Tag] = li;
+				}
+			}
+
+			DialogResult = DialogResult.OK;
+			Close();
+		}
+
+		private void cmdCancel_Click(object sender, EventArgs e)
+		{
+			DialogResult = DialogResult.Cancel;
+			Close();
+		}
+
+		private void grid_DataError(object sender, DataGridViewDataErrorEventArgs e)
+		{
+			e.ThrowException = false;
+		}
+
+		private void grid_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
+		{
+			if (e.ColumnIndex == ColImage.Index)
+			{
+				SkinnedDataGridViewComboBoxCell cell = grid.Rows[e.RowIndex].Cells[e.ColumnIndex] as SkinnedDataGridViewComboBoxCell;
+				if (cell != null)
+				{
+					object v = e.Value;
+					if (v is string)
+					{
+						string name = v.ToString();
+						foreach (object item in cell.Items)
+						{
+							CharacterImage img = item as CharacterImage;
+							if (img != null && img.Name == name)
+							{
+								e.Value = img;
+								e.ParsingApplied = true;
+								break;
+							}
+						}
+					}
+				}
+			}
+		}
+
+		private void grid_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
+		{
+			if (e.ColumnIndex == ColClear.Index)
+			{
+				Image img = Properties.Resources.Delete;
+				e.Paint(e.CellBounds, DataGridViewPaintParts.All);
+				var w = img.Width;
+				var h = img.Height;
+				var x = e.CellBounds.Left + (e.CellBounds.Width - w) / 2;
+				var y = e.CellBounds.Top + (e.CellBounds.Height - h) / 2;
+
+				e.Graphics.DrawImage(img, new Rectangle(x, y, w, h));
+				e.Handled = true;
+			}
+		}
+
+		private void grid_CellContentClick(object sender, DataGridViewCellEventArgs e)
+		{
+			if (e.RowIndex >= 0 && e.ColumnIndex == ColClear.Index)
+			{
+				DataGridViewCell cell = grid.Rows[e.RowIndex].Cells[ColImage.Index];
+				cell.Value = null;
+			}
+		}
+
+		private void grid_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
+		{
+			SkinnedComboBox cb = e.Control as SkinnedComboBox;
+			if (cb != null)
+			{
+				cb.SelectedIndexChanged -= Cb_SelectedIndexChanged;
+				cb.SelectedIndexChanged += Cb_SelectedIndexChanged;
+			}
+		}
+
+		private void Cb_SelectedIndexChanged(object sender, EventArgs e)
+		{
+			SkinnedComboBox comboBox = sender as SkinnedComboBox;
+			if (grid.SelectedCells.Count == 0)
+			{
+				return;
+			}
+			int stage = (int)grid.SelectedCells[0].OwningRow.Tag;
+			ShowImage(comboBox.SelectedItem?.ToString(), stage);
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Forms/StageImageSelection.resx b/editor source/SPNATI Character Editor/Forms/StageImageSelection.resx
new file mode 100644
index 0000000000000000000000000000000000000000..b1cd9321196c67d07c19ef5fb099ef5f61c0cd93
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Forms/StageImageSelection.resx	
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="ColStage.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="ColImage.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="ColClear.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+</root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/StageSelect.Designer.cs b/editor source/SPNATI Character Editor/Forms/StageSelect.Designer.cs
index 46d9243fca4f6825d4899954f082e7f32ee70577..b8cb87f3e13d52224495f52ae8d1e3d6add7030d 100644
--- a/editor source/SPNATI Character Editor/Forms/StageSelect.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/StageSelect.Designer.cs	
@@ -28,17 +28,20 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.lblInstructions = new System.Windows.Forms.Label();
-			this.lstStages = new System.Windows.Forms.ListBox();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
+			this.lblInstructions = new Desktop.Skinning.SkinnedLabel();
+			this.lstStages = new Desktop.Skinning.SkinnedListBox();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// lblInstructions
 			// 
 			this.lblInstructions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblInstructions.Location = new System.Drawing.Point(12, 9);
+			this.lblInstructions.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblInstructions.Location = new System.Drawing.Point(12, 36);
 			this.lblInstructions.Name = "lblInstructions";
 			this.lblInstructions.Size = new System.Drawing.Size(260, 39);
 			this.lblInstructions.TabIndex = 0;
@@ -49,8 +52,11 @@
 			this.lstStages.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+			this.lstStages.BackColor = System.Drawing.Color.White;
+			this.lstStages.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstStages.ForeColor = System.Drawing.Color.Black;
 			this.lstStages.FormattingEnabled = true;
-			this.lstStages.Location = new System.Drawing.Point(12, 51);
+			this.lstStages.Location = new System.Drawing.Point(12, 85);
 			this.lstStages.Name = "lstStages";
 			this.lstStages.Size = new System.Drawing.Size(260, 160);
 			this.lstStages.TabIndex = 1;
@@ -58,7 +64,11 @@
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(116, 223);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(125, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 2;
@@ -69,8 +79,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(197, 223);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(206, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 3;
@@ -78,30 +92,42 @@
 			this.cmdCancel.UseVisualStyleBackColor = true;
 			this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click);
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 255);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(284, 30);
+			this.skinnedPanel1.TabIndex = 4;
+			// 
 			// StageSelect
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(284, 258);
+			this.ClientSize = new System.Drawing.Size(284, 285);
 			this.ControlBox = false;
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.lstStages);
 			this.Controls.Add(this.lblInstructions);
 			this.Name = "StageSelect";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "StageSelect";
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.Label lblInstructions;
-		private System.Windows.Forms.ListBox lstStages;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
+		private Desktop.Skinning.SkinnedLabel lblInstructions;
+		private Desktop.Skinning.SkinnedListBox lstStages;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/StageSelect.cs b/editor source/SPNATI Character Editor/Forms/StageSelect.cs
index bc91c3d3fd94297e154212d8d2e670eb5622e69a..076f9405f532d636109aa7ee73f66127a9cbac58 100644
--- a/editor source/SPNATI Character Editor/Forms/StageSelect.cs	
+++ b/editor source/SPNATI Character Editor/Forms/StageSelect.cs	
@@ -1,10 +1,11 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Collections.Generic;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class StageSelect : Form
+	public partial class StageSelect : SkinnedForm
 	{
 		private List<int> _stages = new List<int>();
 
diff --git a/editor source/SPNATI Character Editor/Forms/TagStageSelect.Designer.cs b/editor source/SPNATI Character Editor/Forms/TagStageSelect.Designer.cs
index 440bc832d9cde2cf35826166155e2b4200863d2b..15c29a67687170c96e4c86306e4c93f4ee668dbe 100644
--- a/editor source/SPNATI Character Editor/Forms/TagStageSelect.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/TagStageSelect.Designer.cs	
@@ -28,31 +28,39 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.lblTag = new System.Windows.Forms.Label();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.lstItems = new System.Windows.Forms.ListView();
-			this.cmdSelectAll = new System.Windows.Forms.Button();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.lblTag = new Desktop.Skinning.SkinnedLabel();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.lstItems = new Desktop.Skinning.SkinnedListView();
+			this.cmdSelectAll = new Desktop.Skinning.SkinnedButton();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel2 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel3 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
+			this.skinnedPanel2.SuspendLayout();
+			this.skinnedPanel3.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(12, 37);
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.PrimaryLight;
+			this.label1.Location = new System.Drawing.Point(8, 7);
 			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(214, 13);
+			this.label1.Size = new System.Drawing.Size(77, 13);
 			this.label1.TabIndex = 0;
-			this.label1.Text = "Choose the stages to which this tag applies:";
+			this.label1.Text = "Tag applies to:";
 			// 
 			// lblTag
 			// 
 			this.lblTag.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.lblTag.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.lblTag.Location = new System.Drawing.Point(12, 9);
+			this.lblTag.Level = Desktop.Skinning.SkinnedLabelLevel.Title;
+			this.lblTag.Location = new System.Drawing.Point(12, 2);
 			this.lblTag.Name = "lblTag";
-			this.lblTag.Size = new System.Drawing.Size(260, 28);
+			this.lblTag.Size = new System.Drawing.Size(258, 30);
 			this.lblTag.TabIndex = 1;
 			this.lblTag.Text = "Tag [Tag]";
 			this.lblTag.TextAlign = System.Drawing.ContentAlignment.TopCenter;
@@ -60,7 +68,11 @@
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(116, 226);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.Blue;
+			this.cmdOK.Location = new System.Drawing.Point(125, 4);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 3;
@@ -71,8 +83,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(197, 226);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.Blue;
+			this.cmdCancel.Location = new System.Drawing.Point(206, 4);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 4;
@@ -86,9 +102,10 @@
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
 			this.lstItems.CheckBoxes = true;
-			this.lstItems.Location = new System.Drawing.Point(12, 82);
+			this.lstItems.Location = new System.Drawing.Point(12, 90);
 			this.lstItems.Name = "lstItems";
-			this.lstItems.Size = new System.Drawing.Size(260, 138);
+			this.lstItems.OwnerDraw = true;
+			this.lstItems.Size = new System.Drawing.Size(260, 126);
 			this.lstItems.TabIndex = 5;
 			this.lstItems.UseCompatibleStateImageBehavior = false;
 			this.lstItems.View = System.Windows.Forms.View.List;
@@ -96,43 +113,80 @@
 			// cmdSelectAll
 			// 
 			this.cmdSelectAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdSelectAll.Location = new System.Drawing.Point(197, 53);
+			this.cmdSelectAll.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdSelectAll.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdSelectAll.Flat = false;
+			this.cmdSelectAll.Location = new System.Drawing.Point(192, 2);
 			this.cmdSelectAll.Name = "cmdSelectAll";
-			this.cmdSelectAll.Size = new System.Drawing.Size(75, 23);
+			this.cmdSelectAll.Size = new System.Drawing.Size(88, 23);
 			this.cmdSelectAll.TabIndex = 6;
 			this.cmdSelectAll.Text = "Select All";
 			this.cmdSelectAll.UseVisualStyleBackColor = true;
 			this.cmdSelectAll.Click += new System.EventHandler(this.cmdSelectAll_Click);
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 227);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(284, 30);
+			this.skinnedPanel1.TabIndex = 7;
+			// 
+			// skinnedPanel2
+			// 
+			this.skinnedPanel2.Controls.Add(this.lblTag);
+			this.skinnedPanel2.Location = new System.Drawing.Point(1, 27);
+			this.skinnedPanel2.Name = "skinnedPanel2";
+			this.skinnedPanel2.PanelType = Desktop.Skinning.SkinnedBackgroundType.Primary;
+			this.skinnedPanel2.Size = new System.Drawing.Size(282, 32);
+			this.skinnedPanel2.TabIndex = 8;
+			// 
+			// skinnedPanel3
+			// 
+			this.skinnedPanel3.Controls.Add(this.cmdSelectAll);
+			this.skinnedPanel3.Controls.Add(this.label1);
+			this.skinnedPanel3.Location = new System.Drawing.Point(1, 59);
+			this.skinnedPanel3.Name = "skinnedPanel3";
+			this.skinnedPanel3.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
+			this.skinnedPanel3.Size = new System.Drawing.Size(283, 27);
+			this.skinnedPanel3.TabIndex = 9;
+			// 
 			// TagStageSelect
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(284, 261);
+			this.ClientSize = new System.Drawing.Size(284, 257);
 			this.ControlBox = false;
-			this.Controls.Add(this.cmdSelectAll);
+			this.Controls.Add(this.skinnedPanel3);
+			this.Controls.Add(this.skinnedPanel2);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.lstItems);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
-			this.Controls.Add(this.lblTag);
-			this.Controls.Add(this.label1);
 			this.Name = "TagStageSelect";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Select Stages";
+			this.skinnedPanel1.ResumeLayout(false);
+			this.skinnedPanel2.ResumeLayout(false);
+			this.skinnedPanel3.ResumeLayout(false);
+			this.skinnedPanel3.PerformLayout();
 			this.ResumeLayout(false);
-			this.PerformLayout();
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label lblTag;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.ListView lstItems;
-		private System.Windows.Forms.Button cmdSelectAll;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel lblTag;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedListView lstItems;
+		private Desktop.Skinning.SkinnedButton cmdSelectAll;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel2;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel3;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/TagStageSelect.cs b/editor source/SPNATI Character Editor/Forms/TagStageSelect.cs
index e4b1cf0b0a7181cf06f8df1ad2e7a3968aa1207b..dbe5a3eb058dfdd99ce0dddecf45b42bd403eaf2 100644
--- a/editor source/SPNATI Character Editor/Forms/TagStageSelect.cs	
+++ b/editor source/SPNATI Character Editor/Forms/TagStageSelect.cs	
@@ -1,8 +1,9 @@
-using System.Windows.Forms;
+using Desktop.Skinning;
+using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class TagStageSelect : Form
+	public partial class TagStageSelect : SkinnedForm
 	{
 		private BindableTag _bindable;
 
diff --git a/editor source/SPNATI Character Editor/Forms/TrophyForm.Designer.cs b/editor source/SPNATI Character Editor/Forms/TrophyForm.Designer.cs
index 10e0970bedc2ff2896d5ead83e302fae77249e9f..eb76fac58eb899c4d0768f502ddf3ceecfd08c94 100644
--- a/editor source/SPNATI Character Editor/Forms/TrophyForm.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/TrophyForm.Designer.cs	
@@ -28,46 +28,36 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.recId = new Desktop.CommonControls.RecordField();
-			this.label1 = new System.Windows.Forms.Label();
-			this.groupBox1 = new System.Windows.Forms.GroupBox();
-			this.valSet = new System.Windows.Forms.NumericUpDown();
-			this.radSet = new System.Windows.Forms.RadioButton();
-			this.valIncrement = new System.Windows.Forms.NumericUpDown();
-			this.radIncrement = new System.Windows.Forms.RadioButton();
-			this.valDecrement = new System.Windows.Forms.NumericUpDown();
-			this.radDecrement = new System.Windows.Forms.RadioButton();
-			this.radUnlock = new System.Windows.Forms.RadioButton();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.lblText = new System.Windows.Forms.Label();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.groupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.valSet = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.radSet = new Desktop.Skinning.SkinnedRadioButton();
+			this.valIncrement = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.radIncrement = new Desktop.Skinning.SkinnedRadioButton();
+			this.valDecrement = new Desktop.Skinning.SkinnedNumericUpDown();
+			this.radDecrement = new Desktop.Skinning.SkinnedRadioButton();
+			this.radUnlock = new Desktop.Skinning.SkinnedRadioButton();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.lblText = new Desktop.Skinning.SkinnedLabel();
 			this.picPreview = new System.Windows.Forms.PictureBox();
+			this.recId = new Desktop.CommonControls.RecordField();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel2 = new Desktop.Skinning.SkinnedPanel();
 			this.groupBox1.SuspendLayout();
 			((System.ComponentModel.ISupportInitialize)(this.valSet)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.valIncrement)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.valDecrement)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.picPreview)).BeginInit();
+			this.skinnedPanel1.SuspendLayout();
+			this.skinnedPanel2.SuspendLayout();
 			this.SuspendLayout();
 			// 
-			// recId
-			// 
-			this.recId.AllowCreate = false;
-			this.recId.Location = new System.Drawing.Point(76, 12);
-			this.recId.Name = "recId";
-			this.recId.PlaceholderText = null;
-			this.recId.Record = null;
-			this.recId.RecordContext = null;
-			this.recId.RecordKey = null;
-			this.recId.RecordType = null;
-			this.recId.Size = new System.Drawing.Size(150, 20);
-			this.recId.TabIndex = 0;
-			this.recId.UseAutoComplete = true;
-			this.recId.RecordChanged += new System.EventHandler<Desktop.CommonControls.RecordEventArgs>(this.recId_RecordChanged);
-			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(12, 16);
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Primary;
+			this.label1.Location = new System.Drawing.Point(3, 8);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(58, 13);
 			this.label1.TabIndex = 1;
@@ -82,16 +72,19 @@
 			this.groupBox1.Controls.Add(this.valDecrement);
 			this.groupBox1.Controls.Add(this.radDecrement);
 			this.groupBox1.Controls.Add(this.radUnlock);
-			this.groupBox1.Location = new System.Drawing.Point(15, 38);
+			this.groupBox1.Location = new System.Drawing.Point(13, 65);
 			this.groupBox1.Name = "groupBox1";
-			this.groupBox1.Size = new System.Drawing.Size(211, 114);
+			this.groupBox1.Size = new System.Drawing.Size(211, 128);
 			this.groupBox1.TabIndex = 3;
 			this.groupBox1.TabStop = false;
 			this.groupBox1.Text = "Effect";
 			// 
 			// valSet
 			// 
-			this.valSet.Location = new System.Drawing.Point(91, 88);
+			this.valSet.BackColor = System.Drawing.Color.White;
+			this.valSet.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valSet.ForeColor = System.Drawing.Color.Black;
+			this.valSet.Location = new System.Drawing.Point(91, 93);
 			this.valSet.Name = "valSet";
 			this.valSet.Size = new System.Drawing.Size(53, 20);
 			this.valSet.TabIndex = 6;
@@ -99,7 +92,7 @@
 			// radSet
 			// 
 			this.radSet.AutoSize = true;
-			this.radSet.Location = new System.Drawing.Point(6, 88);
+			this.radSet.Location = new System.Drawing.Point(6, 95);
 			this.radSet.Name = "radSet";
 			this.radSet.Size = new System.Drawing.Size(44, 17);
 			this.radSet.TabIndex = 5;
@@ -110,7 +103,10 @@
 			// 
 			// valIncrement
 			// 
-			this.valIncrement.Location = new System.Drawing.Point(91, 65);
+			this.valIncrement.BackColor = System.Drawing.Color.White;
+			this.valIncrement.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valIncrement.ForeColor = System.Drawing.Color.Black;
+			this.valIncrement.Location = new System.Drawing.Point(91, 70);
 			this.valIncrement.Minimum = new decimal(new int[] {
             1,
             0,
@@ -128,7 +124,7 @@
 			// radIncrement
 			// 
 			this.radIncrement.AutoSize = true;
-			this.radIncrement.Location = new System.Drawing.Point(6, 65);
+			this.radIncrement.Location = new System.Drawing.Point(6, 72);
 			this.radIncrement.Name = "radIncrement";
 			this.radIncrement.Size = new System.Drawing.Size(75, 17);
 			this.radIncrement.TabIndex = 3;
@@ -139,7 +135,10 @@
 			// 
 			// valDecrement
 			// 
-			this.valDecrement.Location = new System.Drawing.Point(91, 42);
+			this.valDecrement.BackColor = System.Drawing.Color.White;
+			this.valDecrement.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valDecrement.ForeColor = System.Drawing.Color.Black;
+			this.valDecrement.Location = new System.Drawing.Point(91, 47);
 			this.valDecrement.Minimum = new decimal(new int[] {
             1,
             0,
@@ -157,7 +156,7 @@
 			// radDecrement
 			// 
 			this.radDecrement.AutoSize = true;
-			this.radDecrement.Location = new System.Drawing.Point(6, 42);
+			this.radDecrement.Location = new System.Drawing.Point(6, 49);
 			this.radDecrement.Name = "radDecrement";
 			this.radDecrement.Size = new System.Drawing.Size(80, 17);
 			this.radDecrement.TabIndex = 1;
@@ -169,7 +168,7 @@
 			// radUnlock
 			// 
 			this.radUnlock.AutoSize = true;
-			this.radUnlock.Location = new System.Drawing.Point(6, 19);
+			this.radUnlock.Location = new System.Drawing.Point(6, 26);
 			this.radUnlock.Name = "radUnlock";
 			this.radUnlock.Size = new System.Drawing.Size(59, 17);
 			this.radUnlock.TabIndex = 0;
@@ -181,7 +180,11 @@
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(205, 204);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(214, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 4;
@@ -192,8 +195,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(286, 204);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(295, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 5;
@@ -203,9 +210,10 @@
 			// 
 			// lblText
 			// 
-			this.lblText.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+			this.lblText.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.lblText.Location = new System.Drawing.Point(12, 155);
+			this.lblText.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblText.Location = new System.Drawing.Point(12, 200);
 			this.lblText.Name = "lblText";
 			this.lblText.Size = new System.Drawing.Size(349, 47);
 			this.lblText.TabIndex = 6;
@@ -213,28 +221,64 @@
 			// 
 			// picPreview
 			// 
-			this.picPreview.Location = new System.Drawing.Point(232, 16);
+			this.picPreview.BackColor = System.Drawing.Color.Transparent;
+			this.picPreview.Location = new System.Drawing.Point(232, 65);
 			this.picPreview.Name = "picPreview";
 			this.picPreview.Size = new System.Drawing.Size(128, 128);
 			this.picPreview.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
 			this.picPreview.TabIndex = 7;
 			this.picPreview.TabStop = false;
 			// 
+			// recId
+			// 
+			this.recId.AllowCreate = false;
+			this.recId.Location = new System.Drawing.Point(67, 5);
+			this.recId.Name = "recId";
+			this.recId.PlaceholderText = null;
+			this.recId.Record = null;
+			this.recId.RecordContext = null;
+			this.recId.RecordFilter = null;
+			this.recId.RecordKey = null;
+			this.recId.RecordType = null;
+			this.recId.Size = new System.Drawing.Size(150, 20);
+			this.recId.TabIndex = 0;
+			this.recId.UseAutoComplete = true;
+			this.recId.RecordChanged += new System.EventHandler<Desktop.CommonControls.RecordEventArgs>(this.recId_RecordChanged);
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 253);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(373, 30);
+			this.skinnedPanel1.TabIndex = 8;
+			// 
+			// skinnedPanel2
+			// 
+			this.skinnedPanel2.Controls.Add(this.label1);
+			this.skinnedPanel2.Controls.Add(this.recId);
+			this.skinnedPanel2.Location = new System.Drawing.Point(1, 27);
+			this.skinnedPanel2.Name = "skinnedPanel2";
+			this.skinnedPanel2.PanelType = Desktop.Skinning.SkinnedBackgroundType.Primary;
+			this.skinnedPanel2.Size = new System.Drawing.Size(371, 30);
+			this.skinnedPanel2.TabIndex = 9;
+			// 
 			// TrophyForm
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(373, 239);
+			this.ClientSize = new System.Drawing.Size(373, 283);
 			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel2);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.picPreview);
 			this.Controls.Add(this.lblText);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
 			this.Controls.Add(this.groupBox1);
-			this.Controls.Add(this.label1);
-			this.Controls.Add(this.recId);
 			this.Name = "TrophyForm";
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Unlock Collectible";
@@ -245,26 +289,30 @@
 			((System.ComponentModel.ISupportInitialize)(this.valIncrement)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.valDecrement)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.picPreview)).EndInit();
+			this.skinnedPanel1.ResumeLayout(false);
+			this.skinnedPanel2.ResumeLayout(false);
+			this.skinnedPanel2.PerformLayout();
 			this.ResumeLayout(false);
-			this.PerformLayout();
 
 		}
 
 		#endregion
 
 		private Desktop.CommonControls.RecordField recId;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.GroupBox groupBox1;
-		private System.Windows.Forms.NumericUpDown valSet;
-		private System.Windows.Forms.RadioButton radSet;
-		private System.Windows.Forms.NumericUpDown valIncrement;
-		private System.Windows.Forms.RadioButton radIncrement;
-		private System.Windows.Forms.NumericUpDown valDecrement;
-		private System.Windows.Forms.RadioButton radDecrement;
-		private System.Windows.Forms.RadioButton radUnlock;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.Label lblText;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedGroupBox groupBox1;
+		private Desktop.Skinning.SkinnedNumericUpDown valSet;
+		private Desktop.Skinning.SkinnedRadioButton radSet;
+		private Desktop.Skinning.SkinnedNumericUpDown valIncrement;
+		private Desktop.Skinning.SkinnedRadioButton radIncrement;
+		private Desktop.Skinning.SkinnedNumericUpDown valDecrement;
+		private Desktop.Skinning.SkinnedRadioButton radDecrement;
+		private Desktop.Skinning.SkinnedRadioButton radUnlock;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedLabel lblText;
 		private System.Windows.Forms.PictureBox picPreview;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel2;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/TrophyForm.cs b/editor source/SPNATI Character Editor/Forms/TrophyForm.cs
index 1a78f5a69bd78619d58ca8c998c9777c015b0a8f..8e4a13ce5aa565d42ccb0c3e75a01b2570df2162 100644
--- a/editor source/SPNATI Character Editor/Forms/TrophyForm.cs	
+++ b/editor source/SPNATI Character Editor/Forms/TrophyForm.cs	
@@ -1,4 +1,5 @@
-using SPNATI_Character_Editor.Activities;
+using Desktop.Skinning;
+using SPNATI_Character_Editor.Activities;
 using SPNATI_Character_Editor.DataStructures;
 using System;
 using System.Drawing;
@@ -6,7 +7,7 @@ using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class TrophyForm : Form
+	public partial class TrophyForm : SkinnedForm
 	{
 		private Character _character;
 		private bool _abort;
diff --git a/editor source/SPNATI Character Editor/Forms/WhatsNew.Designer.cs b/editor source/SPNATI Character Editor/Forms/WhatsNew.Designer.cs
index 166e01f99f9f4dc7effce9faefd9f3eed02b72b3..b7186738e77221f16f6ef129c15cf0dde36fd7ce 100644
--- a/editor source/SPNATI Character Editor/Forms/WhatsNew.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Forms/WhatsNew.Designer.cs	
@@ -28,18 +28,24 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.lblVersion = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.lblVersion = new Desktop.Skinning.SkinnedLabel();
 			this.wb = new System.Windows.Forms.WebBrowser();
-			this.cmdOK = new System.Windows.Forms.Button();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.skinnedGroupBox1 = new Desktop.Skinning.SkinnedGroupBox();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel2 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedGroupBox1.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
+			this.skinnedPanel2.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
 			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 16F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.label1.Location = new System.Drawing.Point(12, 9);
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Title;
+			this.label1.Location = new System.Drawing.Point(3, 4);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(229, 26);
 			this.label1.TabIndex = 0;
@@ -50,38 +56,33 @@
 			this.lblVersion.AutoSize = true;
 			this.lblVersion.Font = new System.Drawing.Font("Microsoft Sans Serif", 16F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
 			this.lblVersion.ForeColor = System.Drawing.SystemColors.ControlText;
-			this.lblVersion.Location = new System.Drawing.Point(247, 9);
+			this.lblVersion.Level = Desktop.Skinning.SkinnedLabelLevel.Title;
+			this.lblVersion.Location = new System.Drawing.Point(238, 4);
 			this.lblVersion.Name = "lblVersion";
 			this.lblVersion.Size = new System.Drawing.Size(42, 26);
 			this.lblVersion.TabIndex = 1;
 			this.lblVersion.Text = "3.0";
 			// 
-			// label2
-			// 
-			this.label2.AutoSize = true;
-			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.label2.Location = new System.Drawing.Point(13, 51);
-			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(102, 20);
-			this.label2.TabIndex = 2;
-			this.label2.Text = "What\'s New?";
-			// 
 			// wb
 			// 
 			this.wb.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.wb.Location = new System.Drawing.Point(12, 74);
+			this.wb.Location = new System.Drawing.Point(6, 33);
 			this.wb.MinimumSize = new System.Drawing.Size(20, 20);
 			this.wb.Name = "wb";
-			this.wb.Size = new System.Drawing.Size(1026, 523);
+			this.wb.Size = new System.Drawing.Size(1014, 521);
 			this.wb.TabIndex = 3;
 			this.wb.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.wb_DocumentCompleted);
 			// 
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(963, 603);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(972, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 4;
@@ -89,34 +90,75 @@
 			this.cmdOK.UseVisualStyleBackColor = true;
 			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
 			// 
+			// skinnedGroupBox1
+			// 
+			this.skinnedGroupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedGroupBox1.Controls.Add(this.wb);
+			this.skinnedGroupBox1.Location = new System.Drawing.Point(12, 63);
+			this.skinnedGroupBox1.Name = "skinnedGroupBox1";
+			this.skinnedGroupBox1.Size = new System.Drawing.Size(1026, 560);
+			this.skinnedGroupBox1.TabIndex = 5;
+			this.skinnedGroupBox1.TabStop = false;
+			this.skinnedGroupBox1.Text = "What\'s New?";
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 627);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(1050, 30);
+			this.skinnedPanel1.TabIndex = 6;
+			// 
+			// skinnedPanel2
+			// 
+			this.skinnedPanel2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.skinnedPanel2.AutoScroll = true;
+			this.skinnedPanel2.Controls.Add(this.label1);
+			this.skinnedPanel2.Controls.Add(this.lblVersion);
+			this.skinnedPanel2.Location = new System.Drawing.Point(1, 26);
+			this.skinnedPanel2.Margin = new System.Windows.Forms.Padding(0);
+			this.skinnedPanel2.Name = "skinnedPanel2";
+			this.skinnedPanel2.PanelType = Desktop.Skinning.SkinnedBackgroundType.Primary;
+			this.skinnedPanel2.Size = new System.Drawing.Size(1048, 34);
+			this.skinnedPanel2.TabIndex = 7;
+			// 
 			// WhatsNew
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.ClientSize = new System.Drawing.Size(1050, 631);
+			this.ClientSize = new System.Drawing.Size(1050, 657);
 			this.ControlBox = false;
-			this.Controls.Add(this.cmdOK);
-			this.Controls.Add(this.wb);
-			this.Controls.Add(this.label2);
-			this.Controls.Add(this.lblVersion);
-			this.Controls.Add(this.label1);
+			this.Controls.Add(this.skinnedPanel2);
+			this.Controls.Add(this.skinnedPanel1);
+			this.Controls.Add(this.skinnedGroupBox1);
 			this.Name = "WhatsNew";
+			this.ShowIcon = false;
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Version Update";
 			this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.WhatsNew_FormClosing);
 			this.Load += new System.EventHandler(this.WhatsNew_Load);
+			this.skinnedGroupBox1.ResumeLayout(false);
+			this.skinnedPanel1.ResumeLayout(false);
+			this.skinnedPanel2.ResumeLayout(false);
+			this.skinnedPanel2.PerformLayout();
 			this.ResumeLayout(false);
-			this.PerformLayout();
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label lblVersion;
-		private System.Windows.Forms.Label label2;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedLabel lblVersion;
 		private System.Windows.Forms.WebBrowser wb;
-		private System.Windows.Forms.Button cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedGroupBox skinnedGroupBox1;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel2;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Forms/WhatsNew.cs b/editor source/SPNATI Character Editor/Forms/WhatsNew.cs
index 5bc4ae974ad0415b636c8e1d51b2c433c539400b..bc0152489b2682cce07c798d23462e945e66e6c6 100644
--- a/editor source/SPNATI Character Editor/Forms/WhatsNew.cs	
+++ b/editor source/SPNATI Character Editor/Forms/WhatsNew.cs	
@@ -1,11 +1,12 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.IO;
 using System.Text;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Forms
 {
-	public partial class WhatsNew : Form
+	public partial class WhatsNew : SkinnedForm
 	{
 		public WhatsNew()
 		{
diff --git a/editor source/SPNATI Character Editor/GUIHelper.cs b/editor source/SPNATI Character Editor/GUIHelper.cs
index 060ed3d3250bbf1afbb12c681697dff0ba58973f..36a7600c949e5d08afa748a35b0a5523b3b1e532 100644
--- a/editor source/SPNATI Character Editor/GUIHelper.cs	
+++ b/editor source/SPNATI Character Editor/GUIHelper.cs	
@@ -1,4 +1,6 @@
-using System.Collections.Generic;
+using Desktop.Skinning;
+using System;
+using System.Collections.Generic;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor
@@ -94,41 +96,6 @@ namespace SPNATI_Character_Editor
 			return min + "-" + max;
 		}
 
-		/// <summary>
-		/// Attempts to set a combo box's value to the provided text
-		/// </summary>
-		/// <param name="box"></param>
-		/// <param name="text"></param>
-		public static void SetComboBox(ComboBox box, string text)
-		{
-			box.Text = text;
-		}
-
-		/// <summary>
-		/// Reads the value from a combo box
-		/// </summary>
-		/// <param name="box"></param>
-		/// <returns></returns>
-		public static string ReadComboBox(ComboBox box)
-		{
-			if (box.SelectedItem is Trigger)
-			{
-				return ((Trigger)box.SelectedItem).Tag;
-			}
-			else if (box.SelectedItem is Tag)
-			{
-				return TagDatabase.StringToTag(box.Text);
-			}
-			else if (box.SelectedItem is KeyValuePair<string, string>)
-			{
-				return (string)box.SelectedValue;
-			}
-			string value = box.Text;
-			if (value == "")
-				return null;
-			else return value;
-		}
-
 		public static void SetNumericBox(NumericUpDown box, string value)
 		{
 			if (string.IsNullOrEmpty(value))
@@ -160,6 +127,10 @@ namespace SPNATI_Character_Editor
 		/// <returns></returns>
 		public static string RangeToString(string range)
 		{
+			if (range == null)
+			{
+				return "";
+			}
 			string[] pieces = range.Split('-');
 			if (pieces.Length == 1 || pieces[0] == pieces[1])
 			{
@@ -211,5 +182,34 @@ namespace SPNATI_Character_Editor
 				return $"{min}-{max}";
 			}
 		}
+
+		/// <summary>
+		/// Converts a range string to an interval tuple
+		/// </summary>
+		/// <param name="range"></param>
+		/// <returns></returns>
+		public static Tuple<int, int> ToInterval(string range)
+		{
+			int min;
+			int max;
+			string[] pieces = range.Split('-');
+			if (!int.TryParse(pieces[0], out min))
+			{
+				min = 0;
+			}
+			if (pieces.Length > 1)
+			{
+				if (!int.TryParse(pieces[1], out max))
+				{
+					max = min;
+				}
+			}
+			else
+			{
+				max = min;
+			}
+			max = Math.Max(min, max);
+			return new Tuple<int, int>(min, max);
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/GifWriter.cs b/editor source/SPNATI Character Editor/GifWriter.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2ee16ef4b767c39d1484a56b53dd17818b6d252b
--- /dev/null
+++ b/editor source/SPNATI Character Editor/GifWriter.cs	
@@ -0,0 +1,202 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+
+namespace SPNATI_Character_Editor
+{
+	/// <summary>
+	/// Creates a GIF using .Net GIF encoding and additional animation headers.
+	/// </summary>
+	public class GifWriter : IDisposable
+	{
+		#region Fields
+		const long SourceGlobalColorInfoPosition = 10,
+			SourceImageBlockPosition = 789;
+
+		readonly BinaryWriter _writer;
+		bool _firstFrame = true;
+		readonly object _syncLock = new object();
+		#endregion
+
+		/// <summary>
+		/// Creates a new instance of GifWriter.
+		/// </summary>
+		/// <param name="OutStream">The <see cref="Stream"/> to output the Gif to.</param>
+		/// <param name="DefaultFrameDelay">Default Delay between consecutive frames... FrameRate = 1000 / DefaultFrameDelay.</param>
+		/// <param name="Repeat">No of times the Gif should repeat... -1 to repeat indefinitely.</param>
+		public GifWriter(Stream OutStream, int DefaultFrameDelay = 500, int Repeat = -1)
+		{
+			if (OutStream == null)
+				throw new ArgumentNullException(nameof(OutStream));
+
+			if (DefaultFrameDelay <= 0)
+				throw new ArgumentOutOfRangeException(nameof(DefaultFrameDelay));
+
+			if (Repeat < -1)
+				throw new ArgumentOutOfRangeException(nameof(Repeat));
+
+			_writer = new BinaryWriter(OutStream);
+			this.DefaultFrameDelay = DefaultFrameDelay;
+			this.Repeat = Repeat;
+		}
+
+		/// <summary>
+		/// Creates a new instance of GifWriter.
+		/// </summary>
+		/// <param name="FileName">The path to the file to output the Gif to.</param>
+		/// <param name="DefaultFrameDelay">Default Delay between consecutive frames... FrameRate = 1000 / DefaultFrameDelay.</param>
+		/// <param name="Repeat">No of times the Gif should repeat... -1 to repeat indefinitely.</param>
+		public GifWriter(string FileName, int DefaultFrameDelay = 500, int Repeat = -1)
+			: this(new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read), DefaultFrameDelay, Repeat) { }
+
+		#region Properties
+		/// <summary>
+		/// Gets or Sets the Default Width of a Frame. Used when unspecified.
+		/// </summary>
+		public int DefaultWidth { get; set; }
+
+		/// <summary>
+		/// Gets or Sets the Default Height of a Frame. Used when unspecified.
+		/// </summary>
+		public int DefaultHeight { get; set; }
+
+		/// <summary>
+		/// Gets or Sets the Default Delay in Milliseconds.
+		/// </summary>
+		public int DefaultFrameDelay { get; set; }
+
+		/// <summary>
+		/// The Number of Times the Animation must repeat.
+		/// -1 indicates no repeat. 0 indicates repeat indefinitely
+		/// </summary>
+		public int Repeat { get; }
+		#endregion
+
+		/// <summary>
+		/// Adds a frame to this animation.
+		/// </summary>
+		/// <param name="Image">The image to add</param>
+		/// <param name="Delay">Delay in Milliseconds between this and last frame... 0 = <see cref="DefaultFrameDelay"/></param>
+		public void WriteFrame(Image Image, int Delay = 0)
+		{
+			lock (_syncLock)
+				using (var gifStream = new MemoryStream())
+				{
+					Image.Save(gifStream, ImageFormat.Gif);
+
+					// Steal the global color table info
+					if (_firstFrame)
+						InitHeader(gifStream, _writer, Image.Width, Image.Height);
+
+					WriteGraphicControlBlock(gifStream, _writer, Delay == 0 ? DefaultFrameDelay : Delay);
+					WriteImageBlock(gifStream, _writer, !_firstFrame, 0, 0, Image.Width, Image.Height);
+				}
+
+			if (_firstFrame)
+				_firstFrame = false;
+		}
+
+		#region Write
+		void InitHeader(Stream SourceGif, BinaryWriter Writer, int Width, int Height)
+		{
+			// File Header
+			Writer.Write("GIF".ToCharArray()); // File type
+			Writer.Write("89a".ToCharArray()); // File Version
+
+			Writer.Write((short)(DefaultWidth == 0 ? Width : DefaultWidth)); // Initial Logical Width
+			Writer.Write((short)(DefaultHeight == 0 ? Height : DefaultHeight)); // Initial Logical Height
+
+			SourceGif.Position = SourceGlobalColorInfoPosition;
+			Writer.Write((byte)SourceGif.ReadByte()); // Global Color Table Info
+			Writer.Write((byte)0); // Background Color Index
+			Writer.Write((byte)0); // Pixel aspect ratio
+			WriteColorTable(SourceGif, Writer);
+
+			// App Extension Header for Repeating
+			if (Repeat == -1)
+				return;
+
+			Writer.Write(unchecked((short)0xff21)); // Application Extension Block Identifier
+			Writer.Write((byte)0x0b); // Application Block Size
+			Writer.Write("NETSCAPE2.0".ToCharArray()); // Application Identifier
+			Writer.Write((byte)3); // Application block length
+			Writer.Write((byte)1);
+			Writer.Write((short)Repeat); // Repeat count for images.
+			Writer.Write((byte)0); // terminator
+		}
+
+		static void WriteColorTable(Stream SourceGif, BinaryWriter Writer)
+		{
+			SourceGif.Position = 13; // Locating the image color table
+			var colorTable = new byte[768];
+			SourceGif.Read(colorTable, 0, colorTable.Length);
+			Writer.Write(colorTable, 0, colorTable.Length);
+		}
+
+		static void WriteGraphicControlBlock(Stream SourceGif, BinaryWriter Writer, int FrameDelay)
+		{
+			SourceGif.Position = 781; // Locating the source GCE
+			var blockhead = new byte[8];
+			SourceGif.Read(blockhead, 0, blockhead.Length); // Reading source GCE
+
+			Writer.Write(unchecked((short)0xf921)); // Identifier
+			Writer.Write((byte)0x04); // Block Size
+			Writer.Write((byte)(blockhead[3] & 0xf7 | 0x08)); // Setting disposal flag
+			Writer.Write((short)(FrameDelay / 10)); // Setting frame delay
+			Writer.Write(blockhead[6]); // Transparent color index
+			Writer.Write((byte)0); // Terminator
+		}
+
+		static void WriteImageBlock(Stream SourceGif, BinaryWriter Writer, bool IncludeColorTable, int X, int Y, int Width, int Height)
+		{
+			SourceGif.Position = SourceImageBlockPosition; // Locating the image block
+			var header = new byte[11];
+			SourceGif.Read(header, 0, header.Length);
+			Writer.Write(header[0]); // Separator
+			Writer.Write((short)X); // Position X
+			Writer.Write((short)Y); // Position Y
+			Writer.Write((short)Width); // Width
+			Writer.Write((short)Height); // Height
+
+			if (IncludeColorTable) // If first frame, use global color table - else use local
+			{
+				SourceGif.Position = SourceGlobalColorInfoPosition;
+				Writer.Write((byte)(SourceGif.ReadByte() & 0x3f | 0x80)); // Enabling local color table
+				WriteColorTable(SourceGif, Writer);
+			}
+			else Writer.Write((byte)(header[9] & 0x07 | 0x07)); // Disabling local color table
+
+			Writer.Write(header[10]); // LZW Min Code Size
+
+			// Read/Write image data
+			SourceGif.Position = SourceImageBlockPosition + header.Length;
+
+			var dataLength = SourceGif.ReadByte();
+			while (dataLength > 0)
+			{
+				var imgData = new byte[dataLength];
+				SourceGif.Read(imgData, 0, dataLength);
+
+				Writer.Write((byte)dataLength);
+				Writer.Write(imgData, 0, dataLength);
+				dataLength = SourceGif.ReadByte();
+			}
+
+			Writer.Write((byte)0); // Terminator
+		}
+		#endregion
+
+		/// <summary>
+		/// Frees all resources used by this object.
+		/// </summary>
+		public void Dispose()
+		{
+			// Complete File
+			_writer.Write((byte)0x3b); // File Trailer
+
+			_writer.BaseStream.Dispose();
+			_writer.Dispose();
+		}
+	}
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Help/advanced.html b/editor source/SPNATI Character Editor/Help/advanced.html
new file mode 100644
index 0000000000000000000000000000000000000000..2e972cac190885177b305950bb0a474ae653758d
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Help/advanced.html	
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+
+<html lang='en' xmlns='http://www.w3.org/1999/xhtml'>
+<head>
+    <meta charset='utf-8' />
+    <link rel="stylesheet" type="text/css" href="topic.css" />
+    <title>Advanced Metadata</title>
+</head>
+<body>
+    <header id="header">Advanced Metadata</header>
+    <article id="main">
+        <section class="card" id="overview">
+            <h1>Overview</h1>
+            The Advanced tab contains some lesser used options when building a character.
+        </section>
+        <section class="card" id="metadata">
+            <h1>Advanced Metadata</h1>
+            <ul>
+                <li><span class="label">Vary label by stage:</span> This allows you to change what other characters call your character based on their clothing stage. For example, Mulan is initially known as Ping but switches to Mulan after revealing her secret identity. You do not need to fill out every stage. If a stage is left blank, the previous stage's label is used.</li>
+                <li><span class="label">Scale factor:</span> This provides global scaling to your images in the game. Perhaps you made your images too big and don't want to regenerate all of them. You could instead scale them down here.</li>
+                <li><span class="label">Speech bubble position:</span> For talkative characters, it's possible for the speech bubble and the character model to overlap. Normally the bubble appears behind the character, blocking the text. You can use this to put the speech bubble in front of your character.</li>
+                <li><span class="label">Z layer:</span> This adjusts the sorting layers between players. If you are making a custom pose that extends into other character's spaces, you may want to apply a negative z layer so as to ensure your pose appears behind everybody else.</li>
+            </ul>
+        </section>
+        <section class="card" id="nicknames">
+            <h1>Nicknames</h1>
+            While the Label in the section above is what other characters call yours, nicknames are what other characters call yours when using the ~name~ variable. You can assign multiple nicknames to the same character, and the game will choose a random one each time it plays a line with ~name~.<br />
+            If you want to assign one or more nicknames to the entire roster, use the second table. Characters with specific nicknames assigned will have priority over this global list.
+            <div class="info">
+                <h5><i class="fa fa-info"></i>Dynamic Nicknames</h5>
+                You can also nicknames to characters in the middle of the game by using a special per-target marker called nickname. To use this in dialogue, rather than ~name~, use ~marker.nickname~. The following example will cause your character to call the target of the line where this marker was set "Biggest Loser" for the remainder of the game.<br />
+
+                <img src="images/nickname.png" /><br />
+            </div>
+        </section>
+        <section class="card" id="styles">
+            <h1>Custom Text Formatting</h1>
+            For added expressiveness, it's possible to add custom text formatting to your speech bubbles via custom styles. The game includes a handful of prebuilt styles available for all characters to use, but it's also possible to add styles unique to your character.
+            You can find this on your character's Advanced tab.<br />
+            <img src="images/text_formatting.png" />
+            <br />
+            <h3>Style Usage</h3>
+            To enable custom formatting in dialogue, type the style name in curly brackets. To remove all custom formatting mid-line, use {!reset}. For instance, the line "This text is {b}bold{!reset} and {i}italicized{!reset}." will produce "This text is <strong>bold</strong> and <em>italicized</em>" when displayed in game.
+            <h3>Creating Styles</h3>
+            Click the + in this section to create a style. The very first style will be called "example" and will turn text blue. Additional styles will start out being called just "style". To edit a style, select it from the list.
+            <br />
+            <ul>
+                <li><span class="label">Style name:</span> The name is how your style is accessed in dialogue. For instance a style named "example" is used in dialogue by typing {example}. Style names cannot contain spaces or special characters.</li>
+                <li><span class="label">Description:</span> This is used by intellisense when typing { in dialogue to describe what the style does. It has no effect in game.</li>
+            </ul>
+            To configure your style, click +Add to choose a property you want to modify (font, color, size, text effects, etc.). There is no limit to how many properties a single style modifies. For instance, you can use a single style to create small, bold, underlined green text.<br />
+            Once the property has been added, fields appear for customizing it. The preview at the bottom updates immediately to give an idea how it would look in game.<br/>
+            <h3>Miscellaneous Properties</h3>
+            Custom styles are really just CSS behind the scenes. You can use the "Other" style property type to add arbitrary CSS properties to your style. Note that the preview in the editor uses IE11, which is missing some of the more modern CSS properties. Therefore, it's possible that some effects won't appear in the editor but will still show in game when using a modern browser.
+            <br />
+            <h3>Global Styles</h3>
+            The game has several common styles available to all characters already. These are used the same way as character-specific custom styles. Global styles include:
+            <ul>
+                <li><span class="label">{b}:</span> Bold</li>
+                <li><span class="label">{i}:</span> Italics</li>
+                <li><span class="label">{s}:</span> Strikethrough</li>
+                <li><span class="label">{u}:</span> Underline</li>
+                <li><span class="label">{highlight}:</span> Applies a highlighter effect to text</li>
+                <li><span class="label">{mono}:</span> Monospace font (like a robot talking)</li>
+                <li><span class="label">{small}:</span> Slightly smaller text</li>
+                <li><span class="label">{big}:</span> Slightly larger text</li>
+            </ul>
+        </section>
+    </article>
+</body>
+</html>
diff --git a/editor source/SPNATI Character Editor/Help/collectibles.html b/editor source/SPNATI Character Editor/Help/collectibles.html
index c914fc4fffb2ca45ca466afc296120cc9978093a..2f18c0cdd04323dd2f570fd76b2736a084f2f1e2 100644
--- a/editor source/SPNATI Character Editor/Help/collectibles.html	
+++ b/editor source/SPNATI Character Editor/Help/collectibles.html	
@@ -8,7 +8,7 @@
     <title>Collectibles</title>
 </head>
 <body>
-    <header id="header">Macros</header>
+    <header id="header">Collectibles</header>
     <article id="main">
         <section class="card">
             <h1>What is a Collectible?</h1>
@@ -28,7 +28,7 @@
             <img src="images/collectible_screen.png" /><br />
             Collectibles are configured in the Collectibles tab of a character's workspace.
             <ul>
-                <li>The left column is a list of all collectibles for the open character. Press the + to create a new collectibe, or - to delete one.</li>
+                <li>The left column is a list of all collectibles for the open character. Press the + to create a new collectible, or - to delete one.</li>
                 <li>The middle column contains details about the selected collectible.</li>
                 <li>The right column is a preview of the collectible's image that is viewable in game once it's been unlocked.</li>
             </ul>
@@ -50,7 +50,7 @@
         </section>
         <section class="card">
             <h1>Unlocking Collectibles</h1>
-            Collectibles are unlocked via dialogue. You can attach a collectible to a particular line of dialogue, and the collectible will either unlock or progress (or regress!) when the line is played.<br />
+            Collectibles are unlocked via dialogue. You can attach a collectible to a particular line of dialogue, and the collectible will either unlock or its progress meter will progress (or regress!) when the line is played.<br />
             <img src="images/collectible_line.png" />
             <p>
                 Lines that modify a collectible have a colored trophy. Lines that do not touch collectibles have a gray trophy. To attach a collectible to the line, click the trophy.
diff --git a/editor source/SPNATI Character Editor/Help/conditions.html b/editor source/SPNATI Character Editor/Help/conditions.html
new file mode 100644
index 0000000000000000000000000000000000000000..18938c212ef1f9be2a0ca894831aa661db44bf05
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Help/conditions.html	
@@ -0,0 +1,178 @@
+<!DOCTYPE html>
+
+<html lang='en' xmlns='http://www.w3.org/1999/xhtml'>
+<head>
+    <meta http-equiv='X-UA-Compatible' content='IE=edge' />
+    <meta charset='utf-8' />
+    <link rel="stylesheet" type="text/css" href="topic.css" />
+    <title>Conditions</title>
+</head>
+<body>
+    <header id="header">Conditions</header>
+    <article id="main">
+        <section class="card">
+            <h1>Overview</h1>
+            Conditions do exactly what you'd think - make cases conditional based on certain criteria. By using conditions wisely, you can make your character act in a much more convincing way than just filling out the bare minimum generic lines for each phase.<br />
+            You can add conditions to a case in three ways:
+            <ol>
+                <li>Using a pre-built recipe with the flask in the dialogue tree.</li>
+                <li>Searching for a condition type using the "Add a condition" field near the middle of the case editor.</li>
+                <li>Choosing a condition from the various Speed Button menus next to the search field. This is the fastest method once you know what you're looking for.</li>
+            </ol>
+            Each condition type is summarized below, categorized by which Speed Button they appear under.
+        </section>
+        <section class="card">
+            <h1>Also Playing</h1>
+            These conditions revolve around another character at the table other than yours, and other than the current phase target (ex. during a stripping phase, the target is the character stripping).
+            <ul>
+                <li><span class="label">Also Playing:</span> Checks that the chosen character is playing with yours. This is required for all other Also Playing-type conditions to function.</li>
+                <li><span class="label">Also Playing Stage:</span> Checks that the "Also Playing" character is within a certain state of undress. For instance, they have lost their socks but not their shirt yet. Since each character has a different stripping order, the options here vary depending on which character is plugged into "Also Playing".</li>
+                <li><span class="label">Also Playing Said Marker:</span> Checks that the "Also Playing" character has said a particular marker at some earlier point in the game. Refer to the Markers section in the Dialogue help for more information about markers.</li>
+                <li><span class="label">Also Playing Not Said Marker:</span> Checks that the "Also Playing" character has not said a particular marker yet.</li>
+                <li><span class="label">Also Playing Saying Marker:</span> Checks if the "Also Playing" character is saying a line with a particular marker <i>right this moment</i>.</li>
+                <li><span class="label">Also Playing Saying Text:</span> Checks if the "Also Playing" character is saying something <i>right this moment</i> containing this chunk of case-sensitive text.</li>
+                <li><span class="label">Also Playing Time In Stage:</span> Checks how many rounds the "Also Playing" character has been in their current stage (i.e. how long since they last lost).</li>
+                <li><span class="label">Also Playing Hand:</span> Checks that the "Also Playing" character has a particular poker hand.</li>
+            </ul>
+        </section>
+        <section class="card">
+            <h1>Game</h1>
+            These conditions are for game-wide events.
+            <ul>
+                <li><span class="label">Consecutive Losses:</span> For phases with a target, this checks how many rounds in a row that target has lost now. For phases without a target, this checks how many rounds your character has lost in a row.</li>
+                <li><span class="label">Total Rounds:</span> Checks that the game has been through a certain number of completed rounds. Therefore, to condition on the first round of the game, Total Rounds would be 0, not 1.</li>
+                <li><span class="label">Variable Test:</span> Checks that the contents of a variable matches an expected value. See the Variables section for a more detailed list. You can add multiple of these. </li>
+                <li><span class="label">Background:</span> Checks what venue you are playing at.</li>
+                <li><span class="label">Indoors/outdoors:</span> Checks more generically than background whether you are at an indoor or outdoor locale.</li>
+            </ul>
+        </section>
+        <section class="card">
+            <h1>Self</h1>
+            These conditions target information about your own character.
+            <ul>
+                <li><span class="label">Said Marker:</span> Checks if your character has said a marker at an earlier point in the game. Refer to the Markers section in the Dialogue help for more details about markers.</li>
+                <li><span class="label">Not Said Marker:</span> Checks that your character has not said a marker yet in this game.</li>
+                <li><span class="label">Time In Stage:</span> Checks that your character has been in the current stage for a certain number of rounds. In other words, how long since they last lost.</li>
+                <li><span class="label">Has Hand:</span> Checks that your character has a particular poker hand.</li>
+                <li><span class="label">Play Once:</span> This is not a true condition. Rather, it marks this case as only being valid once per game. If a line from it plays once, it will be disqualified from consideration in all future phases. Useful for limiting repeated dialogue.</li>
+                <li><span class="label">Hidden:</span> This is not a true condition. Rather, if all other conditions are valid, then the lines from this case will "play" silently in the background, setting any markers or other advanced changes (changing label, gender, AI, etc.), before a non-hidden case is then chosen to play in the foreground. This allows you to set multiple markers at once, for one thing.</li>
+            </ul>
+        </section>
+        <section class="card">
+            <h1>Table</h1>
+            These conditions target information about the overall state of the poker table (i.e. the general demographics of the people playing)
+            <ul>
+                <li><span class="label">Filter:</span> Checks advanced conditions about the state of the table. Refer to the Filters section below for more details.</li>
+                <li><span class="label">Total Females:</span> Checks that the number of females (including the human and your own character if applicable) falls within a certain range.</li>
+                <li><span class="label">Total Males:</span> Checks that the number of males (including the human and your own character if applicable) falls within a certain range.</li>
+                <li><span class="label">Total Playing:</span> Checks that the number of people still active in the game (including the human and your own character if applicable) falls within a certain range.</li>
+                <li><span class="label">Total Exposed:</span> Checks that the number of people revealing their chest or crotch (including the human and your own character if applicable) falls within a certain range.</li>
+                <li><span class="label">Total Naked:</span> Checks that the number of people completely naked (including the human and your own character if applicable) falls within a certain range.</li>
+                <li><span class="label">Total Masturbating:</span> Checks that the number of people actively masturbating (including the human and your own character if applicable) falls within a certain range.</li>
+                <li><span class="label">Total Finished:</span> Checks that the number of people completely finished and out of the game (including the human and your own character if applicable) falls within a certain range.</li>
+                <li><span class="label">Human Name:</span> Checks that the human player has a particular name.</li>
+            </ul>
+        </section>
+        <section class="card">
+            <h1>Target</h1>
+            These conditions target information about the current phase's target when there is one. The target is the player who lost the hand, is stripping, or is masturbating and people are reacting.
+            <ul>
+                <li><span class="label">Target:</span> Checks that the chosen character is the current target.</li>
+                <li><span class="label">Target Tag:</span> Checks that the target has a certain tag.</li>
+                <li><span class="label">Target Stage:</span> Checks that the target is within a certain state of undress. For instance, they have lost their socks but not their shirt yet. Since each character has a different stripping order, the options here vary depending on which character is plugged into "Target".</li>
+                <li><span class="label">Target Said Marker:</span> Checks that the target has said a particular marker at some earlier point in the game. Refer to the Markers section in the Dialogue help for more information about markers.</li>
+                <li><span class="label">Target Not Said Marker:</span> Checks that the target has not said a particular marker yet.</li>
+                <li><span class="label">Target Saying Marker:</span> Checks if the target is saying a line with a particular marker <i>right this moment</i>.</li>
+                <li><span class="label">Target Saying Text:</span> Checks if the target is saying something <i>right this moment</i> containing this chunk of case-sensitive text.</li>
+                <li><span class="label">Target Time In Stage:</span> Checks how many rounds the target has been in their current stage (i.e. how long since they last lost).</li>
+                <li><span class="label">Target Hand:</span> Checks that the target has a particular poker hand.</li>
+                <li><span class="label">Target Status:</span> Checks that the target's current state of dress matches a particular condition.</li>
+                <li><span class="label">Target Layers:</span> Checks that the target has a certain number of clothing layers remaining.</li>
+                <li><span class="label">Target Starting Layers:</span> Checks that the target started the game with a certain number of layers of clothing.</li>
+                <li><span class="label">Gender:</span> Checks that the target's gender matches a certain criterion.</li>
+                <li><span class="label">Size:</span> Checks that the target's chest or crotch size matches a certain criterion.</li>
+            </ul>
+        </section>
+        <section class="card">
+            <h1>Player</h1>
+            Everything under the Player category is used to target different information about a player. Each condition takes a player you want to test information about, which can be:
+            <ul>
+                <li>Self - Tests information about yourself</li>
+                <li>Target - Tests information about the current target if there is one</li>
+                <li>Character - Fill in any character's name to test information about that character if they are at the table.</li>
+            </ul>
+            Player conditions include:
+            <ul>
+                <li><span class="label">Collectible:</span> Tests if the user has unlocked a particular collectible that belongs to a character.</li>
+                <li><span class="label">Collectible:</span> For collectibles that use counters, this tests what level a collectible's counter is currently at. For example, with a collectible whose unlock condition is "win 5 games with Monika", you could use this to test that they've currently won 2.</li>
+                <li><span class="label">Costume:</span> For characters that have alternate outfits, this tests which outfit they are wearing. To test if a character is not wearing an alternate outfit, you can check if their costume == default.</li>
+                <li><span class="label">Largest lead:</span> This tests what was the largest lead a character has held at any point in the game. A lead is determined by comparing layers of clothing left. So if your character has 5 layers remaining, and the other players have 2, 0, and 3, your character's largest lead is 2 (5-3).</li>
+                <li><span class="label">Layer difference:</span> This tests the difference in remaining clothing layers between two players. For instance, if you have 2 layers left and Florina has 4, your layer difference is -2 (2 - 4).</li>
+                <li><span class="label">Marker:</span> Tests if the player has said a marker. Functionally this is similar to the "Said Marker" condition, but unlike "Said Marker" you can add multiple of these to check for multiple markers.</li>
+                <li><span class="label">Marker (Persistent):</span> Persistent markers last between games. This is the same as the Marker check, except it checks markers that have been tagged as persistent.</li>
+                <li>
+                    <span class="label">Place:</span> Tests various conditions regarding what place your character is in, where place is determined by the number of layers of clothing left. The player with the least layers left is in last place, and the player with the most is in first place. Placement checks include:
+                    <ul>
+                        <li><span class="label">Place:</span> The character's current place, where 1 is first and 5 (in a 5 player game) is last.</li>
+                        <li><span class="label">Reverse place:</span> Like place but inverted, so 1 is last and 5 (in a 5 player game) is first. This is useful to see who is in last place without needing to assume there are 5 players playing.</li>
+                        <li><span class="label">Layers in lead:</span> If the character is currently winning, how many layers remaining they have over 2nd place. If they are not winning, this is how many layers behind first place. (ex. -3 means they are 3 layers behind first place, and 2 means they are 2 layers ahead of 2nd place)</li>
+                        <li><span class="label">Layers behind:</span> If the character is currently losing, how many layers remaining they have over 2nd to last place. If they are not losing, this is how many layers ahead of last place (among players who are still in the game). For example, 2 means they are 2 layers behind 2nd-to-last place, while -3 means they have 3 more layers than last place.</li>
+                        <li><span class="label">Largest lead:</span> The same thing as Player > Largest lead, which is just one less click to get to this condition.</li>
+                    </ul>
+                </li>
+                <li><span class="label">Relative Position:</span> Used to check where a player is standing relative to yours. For instance, you can check that the stripping player is standing to the left. Left in this case means towards the left side of the screen, not the characters' true left.</li>
+                <li><span class="label">Slot:</span> Tests the exact spot a player is standing at where 1 is the far left of the screen and 4 is the far right.</li>
+                <li><span class="label">Collectible:</span> Tests if a player has or does not have a particular tag. This is like Target Tag except you can add multiple tag checks and it works for players other than the current target.</li>
+            </ul>
+        </section>
+        <section class="card">
+            <h1>Clothing</h1>
+            When clothing is being removed, this can test information about that clothing.
+            <ul>
+                <li><span class="label">Clothing Position:</span> Checks that the clothing covers a certain body part.</li>
+                <li><span class="label">Clothing Type:</span> Checks that the clothing is of a certain type (minor, major, etc.)</li>
+            </ul>
+        </section>
+        <section class="card">
+            <h1>Macros</h1>
+            Any user-defined condition macros that were not categorized into another menu can be found here. See the Macros help for more information about macros.
+        </section>
+        <section class="card">
+            <h1>Variable Tests</h1>
+            Variables are placeholder text that gets replaced with other text during the game. For instance, ~name~ gets replaced with the current target's name.
+            While some variables are useful for dialogue, other variables are more useful for checking the state of the game when creating case conditions by using Variable Tests. In fact, a good number of conditions listed above are actually just variable tests in disguise.
+            <br />
+            <strong>Example variable test: </strong> ~clothing.plural~ == plural
+            <br />
+            This condition would hold true if the current article of clothing being stripped has a plural name (ex. socks).<br />
+            Most variables worth testing are given dedicated conditions in the speed menus so that you don't have to worry about the details of the variable name, but here are a few that may be useful that don't have their own conditions:
+            <ul>
+                <li><span class="label">~cards~:</span> This is replaced with the number of cards swapped, so you could test if 0 cards were swapped (meaning the character likely has a really good hand).</li>
+                <li><span class="label">~weekday~:</span> This is replaced with the name of the current day of the week (in real life).</li>
+                <li><span class="label">~clothing~:</span> This is replaced with the name of the clothing being removed, so you can make general reactions to certain types of clothing without needing to target specific characters.</li>
+            </ul>
+        </section>
+        <section class="card">
+            <h1>Filters</h1>
+            Filters are the most powerful type of condition, particularly because they let you create one-time-use variables for dialogue. For instance, you could identify in a table with 3 clothed males and 2 naked females, the name of the other female, store that into a variable, and then in your dialogue reference that variable to name the other female without knowing in advance who that female actually is.<br />
+            Filters also effectively let you use multiple "Also Playing" conditions. <br />
+            A filter takes a range and then one or more other criteria, including:
+            <ul>
+                <li><span class="label">Tag:</span> Targeting that a range of characters have a certain tag. Every character has their own name as a tag, so you can make a filter of 1-1 of tag: X to identify that character X is also playing.</li>
+                <li><span class="label">Gender:</span> Targeting that a range of characters is of a certain gender.</li>
+                <li><span class="label">Status:</span> Targeting that a range of characters is in a certain state of undress.</li>
+            </ul>
+            To see some of these more advanced options, click the double arrows on the right of the filter to expand it. Then click Add Filter to add the filter type you want to use.<br />
+            You can refine your filters to limit them to certain characters using the character and role fields.<br />
+            Finally, you can use "store into variable" to create a variable that will hold the name of one random character that matched the filter's conditions. For instance, if you make a filter with the following settings:
+            <ul>
+                <li>1-5 females</li>
+                <li>Naked</li>
+                <li>Role: Opponent</li>
+                <li>Store into variable: naked</li>
+            </ul>
+            You could write a line of dialogue, "Hey, ~naked~, how's life?" and ~naked~ would be replaced with the name of a naked female character other than the speaker.
+        </section>
+    </article>
+</body>
+</html>
diff --git a/editor source/SPNATI Character Editor/Help/creation.html b/editor source/SPNATI Character Editor/Help/creation.html
index 449aee7cab58e3e21c86893f2081b44cb07df7f1..7e2f69bc611cc7b6fbdc1d7c346add78312b78ac 100644
--- a/editor source/SPNATI Character Editor/Help/creation.html	
+++ b/editor source/SPNATI Character Editor/Help/creation.html	
@@ -11,19 +11,23 @@
     <header id="header">Getting Started</header>
     <ul id="nav">
         <li><a href="#setup">Setup</a></li>
-        <li><a href="#create">Create Character</a></li>
+        <li><a href="#create">Creating a Character</a></li>
     </ul>
     <article id="main">
         <section class="card" id="setup">
             <h1>Setup</h1>
-            The first time you run the Character Editor, it will ask where you have downloaded the GitLab source to.
-            Navigate to the root level of your game folder (i.e. the one containing the js, css, opponents folders, etc.).
+            The first time you run the Character Editor, it will ask where you have downloaded the GitLab source to. If you are reading this, that means you probably figured that step out already, but if not,
+            navigate to the root level of your game folder where you extracted the Git respository download (i.e. usually named spnati.gitlab.io by default, the one containing the js, css, opponents folders, etc.).
             The editor needs to know this information so it knows where to read character information from, among other things.
         </section>
         <section class="card" id="create">
             <h1>Creating a New Character</h1>
-            Once you've finished setup, you are ready to create a new character. Click Characters... to bring up a list of all recognized characters. Type the name of the folder you want to give your character and press enter.
-            Assuming there isn't already a folder of that name, this will enable the "Create New" button. Click this to create your character.
+            Once you've finished setup, you are ready to create a new character. Click File > New and choose a name for your character's <b>folder</b>. Usually this is your character's first name in lowercase unless that is already taken.
+            Click Create.
+            <br />
+            Upon creating your character, it'll inform you of the name of your folder in case it didn't like what you picked.
+            <br/>
+            At this point, you'll be taken to your character's workspace with the metadata tab available. If you save now (File > Save or Ctrl+S), your new character will now be available to play with in the game (offline). To test, open index.html in Firefox (Chrome and Edge do not work for the offline game). Your character should appear at the very end of the character selection screen with a blank box.
         </section>
         <section class="card navCard" id="next">
             <a href="metadata.html">Next: Creating Metadata »</a>
diff --git a/editor source/SPNATI Character Editor/Help/data_analyzer.html b/editor source/SPNATI Character Editor/Help/data_analyzer.html
index df80e4ac4e4a439a4783ac936db1e9e9dfd7e88b..899985c3ab7b2966dc693e13eb6567ce8fe89470 100644
--- a/editor source/SPNATI Character Editor/Help/data_analyzer.html	
+++ b/editor source/SPNATI Character Editor/Help/data_analyzer.html	
@@ -8,7 +8,7 @@
     <title>Data Analyzer</title>
 </head>
 <body>
-    <header id="header">Macros</header>
+    <header id="header">Data Analyzer</header>
     <article id="main">
         <section class="card">
             <h1>Overview</h1>
diff --git a/editor source/SPNATI Character Editor/Help/dialogue.html b/editor source/SPNATI Character Editor/Help/dialogue.html
index ab4b3311f5992d0367cbd47c8d51346619ab9ac0..5f00ef4b9c2ccea45a648064881920344ae5eeab 100644
--- a/editor source/SPNATI Character Editor/Help/dialogue.html	
+++ b/editor source/SPNATI Character Editor/Help/dialogue.html	
@@ -21,113 +21,72 @@
     <article id="main">
         <section class="card" id="basics">
             <h1>Overview</h1>
-            Well-written dialogue with a lot of variation is the key to a successful character. Lines are chosen based on the game's current state, the character's current stage of undress, and optionally other conditions (ex. Character "Y" is also playing in this game).
-            <p>Before making dialogue, you need to be familiar with the terminology:</p>
-            <ul>
-                <li><span class="label">Stage</span>
-                    <p>A stage is the character's current state of undress. Each time they remove an article of clothing, they advance to a new stage. There is always one stage per layer of clothing, plus 3 special stages: naked, masturbating, and finished. The editor creates stages for you based on the Wardrobe tab.</p>
-                    <ul>
-                        <li><span class="label">Naked:</span> The character has lost everything they can strip. The next time they lose a round, they must forfeit (i.e. masturbate).</li>
-                        <li><span class="label">Masturbating:</span> The character remains in this stage until they finish masturbating, decided by the <strong>Rounds to Finish</strong> value on the Metadata tab.</li>
-                        <li><span class="label">Finished:</span> The character remains in this stage from the time they finish masturbating until the very end of the game.</li>
-                    </ul>
-                    <p>Each stage is composed of multiple <strong>cases</strong>.</p>
-                </li>
-                <li><span class="label">Case</span>
-                    <p>A case is a specific game event to react to. For instance, <strong>Must Strip (Male)</strong> is the case that applies when a male opponent has lost the hand and will have to strip.</p>
-                    <p>Which cases can apply depends on the character's stage. For instance, since the player hasn't lost any clothing yet, the <strong>After Stripping</strong> case will never apply to the Fully Dressed stage. The editor automatically creates placeholders for all applicable cases for each stage.</p>
-                    <p>Each case contains one or more lines of dialogue with corresponding poses. If a case is chosen, one of its lines (plus pose) will be chosen at random. This is what will display in the game.</p>
-                    <p>Cases can have conditions applied. For instance, you can condition a <strong>Must Strip (Male)</strong> case to only apply if that opponent is Bob. See the <a href="#targets">Targets</a> section for more info.</p>
-                    <p>A stage can have multiple of the same case. If the cases have no conditions, they will be combined into a single case upon saving.</p>
-                    <p>A case can apply to multiple stages. This means if you edit a case in stage 1, and it's set to apply to stages 1-3, the equivalent case in stages 2 and 3 will be automatically updated as well.</p>
-                    <div class="info">
-                        <i class="fa fa-info"></i>
-                        To see more information about when a case applies, select the case in the editor and a description of that case will appear in the Edit Cases area.
-                        <img src="images/dialogue_case_description.png" title="After Stripping Explanation" />
-                    </div>
-                </li>
-                <li><span class="label">Dialogue Line</span>
-                    <p>A line of dialogue is a pose plus its corresponding case. A case contains one or more lines, from which one will be randomly chosen when the case is used.</p>
-                </li>
-            </ul>
+            This screen is where you'll spend the bulk of your time in the editor, as well-written dialogue with a lot of variation is the key to a successful character.
+
+            <p>Before making dialogue, you need to be familiar with how the game decides what to display.</p>
+            <p>Each round of Poker consists of multiple <b>phases</b> of dialogue:</p>
+            <ol>
+                <li>Characters are dealt their hand and trade out cards. This is the <b>Swapping Cards</b> phase.</li>
+                <li>Characters react to their hands. This is the <b>Hand (Good, Okay, Bad, Any)</b> phase.</li>
+                <li>The character with the worst hand loses. This is the <b>Must Strip</b> phase.</li>
+                <li>The loser starts to strip, or if they have lost all their clothing, starts to masturbate. This is the <b>Stripping</b> phase.</li>
+                <li>The loser finishes stripping. This is the <b>Stripped</b> phase.</li>
+                <li>Another round begins, repeating the same phases.</li>
+            </ol>
+            <p>There are a few other phases that appear at certain points (ex. starting the game or reacting to someone climaxing), but the above is the main game loop.</p>
         </section>
-        <section class="card" id="targets">
-            <h1>Targets</h1>
-            Targeted dialogue allows you to tailor dialogue towards a specific character, or type of character. A case can have one or more conditions attached which limit the case to only being available for use when all conditions are met.
-            Possible conditions include:
-            <ul>
-                <li><span class="label">Target:</span> Conditions that match the target of the current game event (ex. the person who lost the hand). Not all case types have targets. One or more of the following conditions can be used:
-                    <ul>
-                        <li><span class="label">Player:</span> Case will only apply if the target is the given character. This uses the target character's folder name.</li>
-                        <li><span class="label">At stage:</span> The target must be at the given stage. This is a numeric stage ID, but the editor will display friendly labels instead (ex. Lost Shoes) if you have also filled the Player condition.</li>
-                        <li><span class="label">Hand:</span> The target ended up with the given hand (ex. Two Pair, Full House).</li>
-                        <li><span class="label">Tag:</span> The target has the given tag in their <a href="metadata.html">metadata</a>.</li>
-                        <li><span class="label">Time in stage:</span> Number of rounds the target has been in their current stage. This resets to 0 once the target strips, so for a "removed something" case, the time will always be 0, but for a "removing something" case, it will still be counting from their last loss.</li>
-                        <li><span class="label">Consecutive losses:</span> Number of rounds the target has lost in a row.</li>
-                        <li><span class="label">Seen marker:</span> Whether a line of dialogue containing the given marker has been played previously by the target character.</li>
-                        <li><span class="label">Not seen:</span> Whether a line of dialogue containing the given marker has NOT been played previously by the target character.</li>
-                    </ul>
-                </li>
-                <li><span class="label">Other Player:</span> Conditions that match a character currently playing who is <strong>not</strong> the target.
-                    <ul>
-                        <li><span class="label">Player:</span> Case will only apply if the given character is playing. This uses the character's folder name.</li>
-                        <li><span class="label">From/to stage:</span> The character in the Player field must between the given stages. The editor will show user friendly labels, but the underlying values are actually stage IDs. If only the From field is provided, the character must be at that exact stage. If both From and To are filled out, the character must be anywhere between those two stages (inclusive).</li>
-                        <li><span class="label">Hand:</span> The character in the Player field must have the given hand (ex. Two Pair, Full House).</li>
-                        <li><span class="label">Time in stage:</span> Number of rounds the character in the Player field has been in their current stage.</li>
-                        <li><span class="label">Seen marker:</span> Whether a line of dialogue containing the given marker has been played previously by the character in the Player field.</li>
-                        <li><span class="label">Not seen:</span> Whether a line of dialogue containing the given marker has NOT been played previously by the character in the Player field.</li>
-                    </ul>
-                </li>
-                <li><span class="label">Tags:</span> Conditions that target the whole table based on the tags of the characters playing. If multiple conditions are provided, they must all be met for the case to be considered for use in game.
-                    <br /> For example, you can filter the line to display only if 4 players have the "shaved" tag, or filter the line to display only if none of the characters have the "shy" tag.
-                    <ul>
-                        <li><span class="label">Filter on Tag: </span>Chooses a tag to filter on.</li>
-                        <li><span class="label">Total Playing with Tag: </span>Value between 0-4 (including this character but not the human, who can't have tags) for how many characters must have this tag in order for the condition to be met.</li>
-                    </ul>
-                </li>
-                <li><span class="label">Self:</span> Conditions that target this character's current state.
-                    <ul>
-                        <li><span class="label">Own hand:</span> The character being edited must have the given hand (ex. Two Pair, Full House).</li>
-                        <li><span class="label">Time in stage:</span> Number of rounds this player has been in their current stage.</li>
-                        <li><span class="label">Consecutive losses:</span> Number of rounds this player has lost in a row.</li>
-                        <li><span class="label">Seen marker:</span> Whether a line of dialogue containing the given marker has been played previously by this character.</li>
-                        <li><span class="label">Not seen:</span> Whether a line of dialogue containing the given marker has NOT been played previously by this character.</li>
-                    </ul>
-                </li>
-                <li><span class="label">Misc:</span> Conditions that do not target another player directly.
-                    <ul>
-                        <li><span class="label">Game rounds:</span> Number of overall rounds that have passed since the start of the game. A round begins whenever new cards are dealt.</li>
-                        <li><span class="label">Priority:</span> You can assign a case a custom <a href="#priority">priority</a>. This is useful for either putting a targeted case at the same priority as a non-targeted case (so they both get picked from randomly), or to ensure that a case always takes priority over all other possible types of targets.</li>
-                        <li><span class="label">Total females:</span> Number of females playing must match this (including this character and the human player).</li>
-                        <li><span class="label">Total males:</span> Number of males playing must match this (including this character and the human player).</li>
-                        <li><span class="label">Total playing:</span> Number of players still in the game (including this character and the human player). A player is considered in the game until they lose while already naked.</li>
-                        <li><span class="label">Total exposed:</span> Number of players who have exposed their chest or crotch (including this character and the human player).</li>
-                        <li><span class="label">Total naked:</span> Number of players who are fully naked (including this character and the human player).</li>
-                        <li><span class="label">Total masturbating:</span> Number of players who are currently masturbating (including this character and the human player).</li>
-                        <li><span class="label">Total finished:</span> Number of players who have finished masturbating and are out of the game (including this character and the human player).</li>
-                    </ul>
-                </li>
-            </ul>
-            <div class="info">
-                <h5><i class="fa fa-info"></i>Human Interaction</h5>
-                You can target the human player by using "human" as your target in the Player fields.
-            </div>
+        <section class="card" id="logic">
+            <h1>Choosing Dialogue</h1>
+            <p>
+                For each phase, one line of dialogue is chosen to play for each character. Every line of dialogue has a corresponding image that is displayed with it.
+            </p>
+            <p>To decide which dialogue line to use, for each character the game performs the following steps:</p>
+            <ol>
+                <li>It filters out only those <b>cases</b> that are applicable to the current game phase. A <b>case</b> is a group of dialogue lines corresponding to a particular phase.</li>
+                <li>Of these cases, it then throws out any cases that do not apply to the character's current <b>stage</b> of undress. A single cases can apply to multiple stages.</li>
+                <li>Of the remaining cases, it then looks at any conditions attached to those cases, and filters them out if their conditions don't apply. For instance, a case may have a condition to only play if 3 males are present.</li>
+                <li>Finally, for any cases that still remain for selection, the case with the highest priority has a line chosen at random to play.</li>
+            </ol>
+            <p>This is a tad oversimplified, but the full details are not important for most dialogue.</p>
         </section>
         <section class="card" id="priority">
             <h1>Dialogue Priority</h1>
-            When multiple cases fit the current game state's criteria, the game follows a specific algorithm for determining what case to use:
+            Briefly mentioned above is the concept of case priority. All cases have an internal priority to determine which case will play if all conditions are satisfied and multiple cases remain. Priority works as follows:
             <ol>
                 <li>If a case has conditions and those conditions are met, the case will <strong>always</strong> take priority over a case with no conditions.</li>
                 <li>If multiple cases have conditions, they will be given specific priorities based on their conditions. For example, a case with a target and target stage will always take priority over a case with just a target. The editor will sort cases in the order of their priority, and display that priority in the list.</li>
-                <li>If a case has conditions, and those conditions are not met by the current game state, the case will be ignored.</li>
-                <li>If no case has been chosen yet, the case with no conditions (the generic case) will be used.
-                    <div class="info">
-                        <h5><i class="fa fa-info"></i>Generic Cases</h5>
-                        There is always only one generic case per stage. You can have multiple generic cases in a stage (for instance, a case that applies to stages 1-5 and a case that applies to stages 1-3 will show two cases in stage 1), but when saving, these will be combined into a single case where their stages overlap.
-                    </div>
-                </li>
             </ol>
-            <p>The case with the highest priority will then have a random line chosen. If multiple cases have the same priority, a random case will be chosen.</p>
+            <p>The case with the highest priority will then have a random line chosen. If multiple cases have the same priority, the lines from each of these cases will be combined into a single pool and a line will be chosen from that pool at random.</p>
+        </section>
+        <section class="card" id="example">
+            Consider a character who has the following cases:
+            <ul>
+                <li>A. Must Strip - Female</li>
+                <li>B. Hand (Good)</li>
+                <li>C. Hand (Good) when Fully Dressed</li>
+                <li>D. Hand (Good) when hand = Two Pair, custom priority 0</li>
+                <li>E. Hand (Good) when hand = Royal Flush</li>
+                <li>F. Hand (Bad)</li>
+                <li>G. Hand (Any)</li>
+            </ul>
+            <p>The game is at the hand reaction phase. The character has lost a few times and is currently in their <b>Lost Shirt</b> stage. Their hand a Royal Flush. Dialogue is picked as follows:</p>
+            <ol>
+                <li>Cases not applicable to the current phase are thrown out, so case A is discarded.</li>
+                <li>Additionally, the character has a Royal Flush, which is not a Bad Hand, so case F is discarded.</li>
+                <li>The character is not Fully Dressed, so case C is discarded.</li>
+                <li>The character does not have a Two Pair, so case D is discarded.</li>
+                <li>Of the remaining lines, case E has the highest priority, so B and F are discarded.</li>
+                <li>Case E is all that remains, so one of its lines will play.</li>
+            </ol>
+            <p>Consider the same game state except the character now has a Two Pair instead of a Royal Flush</p>
+            <ol>
+                <li>Cases not applicable to the current phase are thrown out, so case A is discarded.</li>
+                <li>Additionally, the character has a Royal Flush, which is not a Bad Hand, so case F is discarded.</li>
+                <li>The character is not Fully Dressed, so case C is discarded.</li>
+                <li>The character does not have a Royal Flush, so case E is discarded.</li>
+                <li>Cases with no conditions (of those remaining: B and G) have a priority of 0. Case D has a condition so its priority would normally be over 0, but for this character a custom override of 0 was given to it.</li>
+                <li>Cases B, D, and G therefore all have the same priority, so their lines will be pooled together and one chosen at random.</li>
+            </ol>
         </section>
         <section class="card" id="legend">
             <h1>Legend</h1>
@@ -136,9 +95,10 @@
         </section>
         <section class="card" id="tree">
             <h1>1. Dialogue Tree</h1>
-            <p>The dialogue tree groups cases by stage. This is the area where you can add, remove, or copy entire cases. When you select a case, the <a href="#case">Case Editor</a> will populated with that case's information.</p>
+            <p>The dialogue tree groups cases by either stage or by case type. This is the area where you can add, remove, or copy entire cases. When you select a case, the <a href="#case">Case Editor</a> will populated with that case's information.</p>
             <ul>
-                <li><span class="label">Add:</span> Adds a new case of the same type as the currently selected case, and add it to the currently selected case's stage.</li>
+                <li><span class="label">Add:</span> Adds a new case of the same type as the currently selected case, and add it to the currently selected case's stage. Alternatively, clicking the dropdown arrow brings up a list of case types for which you can add the type of your choice.</li>
+                <li><span class="label">Add Recipe:</span> The flask brings up a recipe selection, which creates a case with a predefined list of conditions to match a certain game state (eg. conditions to be able to react to the first character having to get naked).</li>
                 <li><span class="label">Copy Tools:</span> Various options for copying or splitting a case among stages, including:
                             <ul>
                                 <li><span class="label">Separate This Stage Into a New Case</span>
@@ -189,10 +149,8 @@
         <section class="card" id="case">
             <h1>2. Case Editor</h1>
             When selecting a case from the <a href="#tree">Dialogue Tree</a>, this area will populate with details from the case.
-            <h2>2a. Case Type</h2>
-            This allows to you change the current case's type. Typically you would only ever need to do this if you're copying a case to another one (ex. Must Strip (Male) to Must Strip (Female))
 
-            <h2>2b. Applies to Stages</h2>
+            <h2>2a. Stages Grid</h2>
             You will often want to repeat lines between stages. While you could manually copy lines to each stage, this would get really repetitive and become a massive pain to modify later.
             To make things easier, the Character Editor allows you to share a case across multiple stages. If a case is shared between stages 1, 2, and 3, it is said that the case <strong>applies to stages</strong> 1-3.
             You can open the case from any of these stages in the tree and any modifications will be automatically applied to the case under each stage.
@@ -205,14 +163,18 @@
             </div>
             <div class="info">
                 <h5><i class="fa fa-info"></i>Variety is Key</h5>
-                While sharing dialogue is very convenient (who really wants to write 15 unique Swapping Cards lines for every stage?), try to maintain a balance between convenience and variety. The more lines you share across stages, the more repetitive your character will be, and the less replayability they will have. Keep note of the Unique Lines of Dialogue counter in the upper right. A good number to shoot for is 350 or more.
+                While sharing dialogue is very convenient (who really wants to write 15 unique Swapping Cards lines for every stage?), try to maintain a balance between convenience and variety. The more lines you share across stages, the more repetitive your character will be, and the less replayability they will have. Keep note of the Unique Lines of Dialogue counter in the upper right. Characters these days have 700+, with some top performers even reaching multiple thousands.
             </div>
 
+            <h2>2b. Case Type</h2>
+            This allows to you change the current case's type. Typically you would only ever need to do this if you're copying a case to another one (ex. Must Strip (Male) to Must Strip (Female)).<br />
+            You can also give your case a custom label, color and folder here. These tools are not used by the game, but help organize your dialogue in the tree.
+
             <h2>2c. Conditions</h2>
-            This is where you set a case's targeted dialogue. See the <a href="#targets">Targets</a> section for more detailed information.
+            This is where you set the conditions that narrow down when a case should be available to play. See the <a href="conditions.html">Conditions</a> section for more detailed information.
             <div class="info">
                 <h5><i class="fa fa-info"></i>Variety is Key</h5>
-                Adding a lot of targeted dialogue is crucial to making a replayable character. The most lively games are those where characters frequently interact with each other.
+                Adding a lot of targeted dialogue is crucial to making a replayable character. The most lively games are those where characters frequently interact with each other and to the overall flow of the game.
             </div>
 
             <h2>2d. Dialogue Editor</h2>
@@ -220,20 +182,31 @@
             <ul>
                 <li><span class="label">Image column:</span> Available images are available for selection here to tie with a line of text. When you choose an image, the Pose Preview will automatically update to show you it. This makes it easy to pick poses and dialogue that fit together.
                     <div class="info">
-                       <h5><i class="fa fa-info"></i>My pose is missing!</h5>
+                        <h5><i class="fa fa-info"></i>My pose is missing!</h5>
                         Which images are available for a case depends on which stages the case applies to. If your case applies to stages 1, 2, and 3 and you want to use the "happy" pose, 1-happy.png, 2-happy.png and 3-happy.png <strong>must all exist</strong> in the character's folder.
                         You will be unable to choose the pose otherwise. This is to help prevent creating dialogue with broken images.
                     </div>
                     <div class="info">
                         <h5><i class="fa fa-info"></i>Animated GIFs</h5>
-                        The Character Editor can only create PNG files, but it is capable of reading GIF files, provided they exist in the characcer folder at the time the editor is launched. As long as the GIFs match the rules outlined in the box above, they can be selected the same way as PNGs.
+                        The Character Editor can only create PNG files, but it is capable of reading GIF files, provided they exist in the character folder at the time the editor is launched. As long as the GIFs match the rules outlined in the box above, they can be selected the same way as PNGs.
+                    </div>
+                    <div class="info">
+                        <h5><i class="fa fa-info"></i>Pose Maker</h5>
+                        Poses created in the Pose Marker will display in the sidebar as well, running through any animation once.
+                    </div>
+                    <div class="info">
+                        <h5><i class="fa fa-info"></i>Controlling the Preview</h5>
+                        Initially a case that is shared across stages will show previews of the earliest stage that is shared. You can view other stages by right-clicking that stage's checkbox in the stages grid.
                     </div>
                 </li>
+                <li><span class="label">Stage-specific images button:</span> The dobule arrows next to the image will bring up a popup where you can pick different poses between shared stages rather than using the same pose name for all stages. This avoids the need to duplicate cases if you want the same text but only a different image.</li>
                 <li><span class="label">Text column:</span> This is the text that will appear in the character's speech bubble when the line is chosen. Line should be fairly short, since a wall of text will both be glazed over by the player and will often not fit in the speech bubble.</li>
-                <li><span class="label">Marker column:</span> When this line is played in game, it track that the line has been played using this "marker." This character or others can then write targeted dialogue based on whether that marker has been seen yet. This allows you to play a line only once all game, for example, or to only play a line if another line was previously played earlier in the game.</li>
-                <li><span class="label">Silent column:</span> If this is checked, no dialogue bubble will be displayed at all.</li>
-                <li><span class="label">Italics:</span> Words can be italicized by encasing them in &lt;i&gt;&lt;/i&gt;. For example, &lt;i&gt;Wow!&lt;/i&gt; will produce <span style="font-style:italic">Wow!</span>.</li>
-                <li><span class="label">Variables:</span> Lines can use variables, which are placeholder labels that will be replaced at runtime with a dynamic bit of text. To use a variable, in the text type ~variableName~ where variableName is the name of the variable. Available variables include:
+                <li><span class="label">Marker column:</span> When this line is played in game, it tracks that the line has been played using this "marker." This character or others can then write targeted dialogue based on whether that marker has been "said" yet. This allows you to play a line only once all game, for example, or to only play a line if another line was previously played earlier in the game.</li>
+                <li><span class="label">Advanced marker button:</span> By default markers are like an on/off toggle. This button brings up more detailed options, such as storing a specific piece of information in the marker that can be retrieved later.</li>
+                <li><span class="label">Collectible button:</span> This button allows you to link a collectible to the line.</li>
+                <li><span class="label">Advanced line options:</span> Side effects of a line playing are found here. For instance, you can change the character's label (name), AI, or even gender if a particular line plays.</li>
+                <li><span class="label">Italics:</span> Words can be italicized by encasing them in &lt;i&gt;&lt;/i&gt;. For example, &lt;i&gt;Wow!&lt;/i&gt; will produce <span style="font-style: italic">Wow!</span>.</li>
+                <li><span class="label">Variables:</span> Lines can use variables, which are placeholder labels that will be replaced at runtime with a dynamic bit of text. To use a variable, in the text type ~variableName~ where variableName is the name of the variable. There is an ever expanding list of variables, but for dialogue, the most important are:
                     <ul>
                         <li><span class="label">~player~: </span>Replaced with the human player's name.</li>
                         <li><span class="label">~name~: </span>Replaced with the target's name, if there is a target.</li>
@@ -242,7 +215,7 @@
                         <li><span class="label">~Clothing~": </span>Same as ~clothing~ but uses the proper name instead of lower case.</li>
                         <li><span class="label">~marker.markerName~: </span>Replaced with the value currently stored in a marker. Replace "markerName" with the name of the marker you want to read.</li>
                     </ul>
-                    Not all variables can be used for all cases. Available variables for the current case appear above the dialogue lines. Additionally, if you try to use a variable that isn't available, it will complain.
+                    Not all variables can be used for all cases. Available variables for the current case can be seen by hovering over the label that tells you as much. Additionally, if you try to use a variable that isn't available, it will complain.
                 </li>
                 <li><span class="label">Copy All:</span> This will copy all of the current case's dialogue lines into the editor's clipboard, useful if you want to copy lines from one case to another.</li>
                 <li><span class="label">Paste All:</span> This will paste previous copied lines into the current case's dialogue. You are given the option to either overwrite the existing dialogue, or add to it.</li>
@@ -252,8 +225,14 @@
             <h1>3. Pose Preview</h1>
             This area displays the image associated with the selected dialogue line in a case.
         </section>
+        <section class="card" id="markers">
+            <h1>Markers</h1>
+            Each phase is by default computed in a complete bubble from everything else that has happened in the game. You can use conditions to figure out important details about the game like who's naked, or has lost several times in row, or so forth, but your character has no memory of their own.<br />
+            You can give your character a memory by way of markers. A marker is like a bookmark to a line of dialogue. When saying a line, the character can set an associated marker (see above), and then for future dialogue, use a condition to check that marker has been said previously.
+            This lets you track more concrete details from a game, such as whether Florina had run away from the table earlier by checking her "fled" marker.
+        </section>
         <section class="card navCard" id="next">
-            <a href="poses.html">« 5. Importing Poses</a> | <a href="endings.html">7. Endings »</a>
+            <a href="poses.html">« Importing Poses</a> | <a href="endings.html"> Endings »</a>
         </section>
     </article>
 </body>
diff --git a/editor source/SPNATI Character Editor/Help/faq.html b/editor source/SPNATI Character Editor/Help/faq.html
index e1e3f1b3738f62e4cc440cedead4408fc58ba0a9..572a2ff3dc0c20ea222fff7ac21cb05195a1d3e0 100644
--- a/editor source/SPNATI Character Editor/Help/faq.html	
+++ b/editor source/SPNATI Character Editor/Help/faq.html	
@@ -19,6 +19,10 @@
             <h1>Why isn't my character showing up anywhere (e.g. the character select menu, target lists, etc.)?</h1>
             If your character isn't in the list, one of their xml files is likely invalid. Check errorlog.txt for more details. You should never run into this issue when editing characters exclusively in the editor, but this can happen if people manually edit the xml.          
         </section>
+        <section class="card">
+            <h1>How do I target the human player?</h1>
+            You can use "human" as the target in the Target condition field.
+        </section>
     </article>
 </body>
 </html>
diff --git a/editor source/SPNATI Character Editor/Help/file.html b/editor source/SPNATI Character Editor/Help/file.html
deleted file mode 100644
index f252c9362b608d82d14bf72f85927c75fef0e834..0000000000000000000000000000000000000000
--- a/editor source/SPNATI Character Editor/Help/file.html	
+++ /dev/null
@@ -1,67 +0,0 @@
-<!DOCTYPE html>
-
-<html lang='en' xmlns='http://www.w3.org/1999/xhtml'>
-<head>
-    <meta http-equiv='X-UA-Compatible' content='IE=edge' />
-    <meta charset='utf-8' />
-    <link rel="stylesheet" type="text/css" href="topic.css" />
-    <title>File Menu</title>
-</head>
-<body>
-    <header id="header">File Menu</header>
-    <ul id="nav">
-        <li><a href="#open">Open</a></li>
-        <li><a href="#save">Save</a></li>
-        <li><a href="#import">Import .txt</a></li>
-        <li><a href="#export">Export .txt</a></li>
-        <li><a href="#setup">Setup</a></li>
-        <li><a href="#exit">Exit</a></li>
-    </ul>
-    <article id="main">
-        <section class="card" id="open">
-            <h1>Open</h1>
-            Use this option to edit an existing character. The editor will automatically open to whatever character was last saved.
-            <div class="info">
-                <h5><i class="fa fa-info"></i>Why isn't my character showing up?</h5>
-                If your character isn't in the list, one of their xml files is likely invalid. Check errorlog.txt for more details. You should never run into this issue when editing characters exclusively in the editor, but this can happen if people manually edit the xml.          
-            </div>
-        </section>
-        <section class="card" id="save">
-            <h1>Save</h1>
-            Use this option to save your changes. This will save data from all tabs besides the Images tab.
-            <p>When saving, the editor will regenerate placeholder lines for any missing tags.</p>
-            <div class="info">
-                <h5><i class="fa fa-info"></i>Auto-backup</h5>
-                The first time you save each day, the editor will create a backup of meta.xml and behaviour.xml. These can be found in your character's folder with the .edit.bak file extension.
-            </div>
-        </section>
-        <section class="card" id="import">
-            <h1>Import .txt</h1>
-            Use this option to pull dialogue from a text file. This works essentially the same way that make_xml.py does.
-            <div class="warning">
-                <i class="fa fa-warning"></i>
-                This will replace ALL data besides images. If you aren't careful, you could lose work.
-                For example, if you add lines in the editor and then import a text file that doesn't contain those lines, they will be lost.
-            </div>
-        </section>
-        <section class="card" id="export">
-            <h1>Export .txt</h1>
-            Use this option to export your character data (excluding images) into a text file. This text file can be run through make_xml.py to regenerate dialogue, or you can
-            import it back into the editor later. This option is primarily for collaboration purposes. If you are making a character on your own, there's little reason to work outside the editor.
-            <p>That said, many experienced creators prefer working with a text file, so when you are ready to submit your character, you should export a text file as well.</p>
-            <div class="info">
-                <i class="fa fa-info"></i>
-                Exporting will create or overwrite edit-dialogue.txt in your character's folder.
-            </div>
-        </section>
-        <section class="card" id="setup">
-            <h1>Setup</h1>
-            Opens the Setup window that appeared the first time you launched the Character Editor.
-        </section>
-        <section class="card" id="exit">
-            <h1>Exit</h1>
-            Many good men and women have lost their lives trying to figure out what this button does.
-        </section>
-    </article>
-</body>
-</html>
diff --git a/editor source/SPNATI Character Editor/Help/help.html b/editor source/SPNATI Character Editor/Help/help.html
index e74107defe890d972c073118c57ea5a0dd618a58..c078f461d4e6c38fe676f981f3297afa7b474079 100644
--- a/editor source/SPNATI Character Editor/Help/help.html	
+++ b/editor source/SPNATI Character Editor/Help/help.html	
@@ -14,19 +14,19 @@
         <ul>
             <li class="topic">General</li>
             <li class="section" onclick="navigate('./overview.html');">Overview</li>
-            <li class="section" onclick="navigate('./file.html');">File Menu</li>
             <li class="section" onclick="navigate('./faq.html');">FAQ</li>
             <li class="topic">Step-by-Step Guide</li>
             <li class="section" onclick="navigate('./creation.html');">Getting Started</li>
             <li class="section" onclick="navigate('./metadata.html');">Metadata</li>
             <li class="section" onclick="navigate('./wardrobe.html');">Wardrobe</li>
+            <li class="section" onclick="navigate('./tags.html');">Tags</li>
             <li class="section" onclick="navigate('./poses.html');">Importing Poses</li>
             <li class="section" onclick="navigate('./posemaker.html');">Pose Maker</li>
             <li class="section" onclick="navigate('./dialogue.html');">Dialogue</li>
             <li class="section" onclick="navigate('./endings.html');">Endings</li>
             <li class="section" onclick="navigate('./collectibles.html');">Collectibles</li>
+            <li class="section" onclick="navigate('./advanced.html');">Advanced</li>
             <li class="topic">Testing</li>
-            <li class="section" onclick="navigate('./dialogue_tester.html');">Dialogue Tester</li>
             <li class="section" onclick="navigate('./validation.html');">Validation</li>
             <li class="section" onclick="navigate('./listing.html');">Adding to the Game</li>
             <li class="topic">Tutorials</li>
@@ -41,6 +41,7 @@
             <li class="section" onclick="navigate('./ending_tutorial3.html');">Epilogues - Fades</li>
             <li class="section" onclick="navigate('./ending_tutorial4.html');">Epilogues - Particles</li>
             <li class="topic">Tools</li>
+            <li class="section" onclick="navigate('./line_slicer.html');">Line Slicer</li>
             <li class="section" onclick="navigate('./data_analyzer.html');">Data Analyzer</li>
         </ul>
     </nav>
diff --git a/editor source/SPNATI Character Editor/Help/images/anger.PNG b/editor source/SPNATI Character Editor/Help/images/anger.PNG
index eb1e3c0a28477bc3ce924a1eb4d82b692ca4fbde..32bc6457754fde516e39063e395c8d9c8ec50075 100644
Binary files a/editor source/SPNATI Character Editor/Help/images/anger.PNG and b/editor source/SPNATI Character Editor/Help/images/anger.PNG differ
diff --git a/editor source/SPNATI Character Editor/Help/images/blush.PNG b/editor source/SPNATI Character Editor/Help/images/blush.PNG
index d4aa683e744330cf3833282dcc33d349054cde31..15d2d00ccd5af0f86950925f322a989fd32651dc 100644
Binary files a/editor source/SPNATI Character Editor/Help/images/blush.PNG and b/editor source/SPNATI Character Editor/Help/images/blush.PNG differ
diff --git a/editor source/SPNATI Character Editor/Help/images/dialogue_case_description.PNG b/editor source/SPNATI Character Editor/Help/images/dialogue_case_description.PNG
index 597c935ba2f1a025d12ed549f32285898ab1ef0b..dd46efe27cd59fb0d82a0cf48c54edd80f15be2e 100644
Binary files a/editor source/SPNATI Character Editor/Help/images/dialogue_case_description.PNG and b/editor source/SPNATI Character Editor/Help/images/dialogue_case_description.PNG differ
diff --git a/editor source/SPNATI Character Editor/Help/images/dialogue_screen.png b/editor source/SPNATI Character Editor/Help/images/dialogue_screen.png
index 4205bafcc6813258324fa4200d643f9c01015432..ef9692721fd949164573b6969915ac9b6ef67b9e 100644
--- a/editor source/SPNATI Character Editor/Help/images/dialogue_screen.png	
+++ b/editor source/SPNATI Character Editor/Help/images/dialogue_screen.png	
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:7e38d439cdd45f0bf8828f49beba38d128091420f1d58a7e264bb7c9ab403518
-size 67312
+oid sha256:1f9d6a05623dba64d6980532661a1e82e700a412f072d164382160276a809740
+size 54001
diff --git a/editor source/SPNATI Character Editor/Help/images/layout.png b/editor source/SPNATI Character Editor/Help/images/layout.png
new file mode 100644
index 0000000000000000000000000000000000000000..07386fabc03797c1d34edf1b1dd95cfdbe3ad5a0
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Help/images/layout.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:13838e22520e48d992aad3a1d2d3aea535ca3ca5c1e3ba6b9e78cb8592a69d22
+size 31638
diff --git a/editor source/SPNATI Character Editor/Help/images/nickname.png b/editor source/SPNATI Character Editor/Help/images/nickname.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb7b11aa2d1bfb54f032937f70c847d03e413893
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Help/images/nickname.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3b17ef0402b03311add5a31e23b80d9aa81732868afd3a7d6d5962d2e3d59ae5
+size 3743
diff --git a/editor source/SPNATI Character Editor/Help/images/slicer.png b/editor source/SPNATI Character Editor/Help/images/slicer.png
new file mode 100644
index 0000000000000000000000000000000000000000..6f15ce98aa617caee7ad06efa779c57a61f76d2f
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Help/images/slicer.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:97b7734ae159b34a92e324389cab0f1bb94c6919d6f8408b8e2a21c104b8f33b
+size 10079
diff --git a/editor source/SPNATI Character Editor/Help/images/text_formatting.png b/editor source/SPNATI Character Editor/Help/images/text_formatting.png
new file mode 100644
index 0000000000000000000000000000000000000000..9b4845245a957583ec4ffffa6f01fd50f40727e8
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Help/images/text_formatting.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:05d1b1fa422fe88c737697c45a50a97b381545a40eb33861a2360b988ea0137a
+size 6120
diff --git a/editor source/SPNATI Character Editor/Help/line_slicer.html b/editor source/SPNATI Character Editor/Help/line_slicer.html
new file mode 100644
index 0000000000000000000000000000000000000000..d41b74c86727fc106a945edf44eed2bac85c5059
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Help/line_slicer.html	
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+
+<html lang='en' xmlns='http://www.w3.org/1999/xhtml'>
+<head>
+    <meta http-equiv='X-UA-Compatible' content='IE=edge' />
+    <meta charset='utf-8' />
+    <link rel="stylesheet" type="text/css" href="topic.css" />
+    <title>Line Slicer</title>
+</head>
+<body>
+    <header id="header">Line Slicer</header>
+    <article id="main">
+        <section class="card">
+            <h1>Overview</h1>
+            <p>
+                The Line Slicer is a reporting tool (found under the tabs in a character's workspace) used to get at-a-glance information about what game conditions you're covering in your dialogue. For instance, you can use this tool to view how many lines are targeting male "must strip" situations vs female.
+            </p>
+            <img src="images/slicer.png" />
+            
+            <p>The three main components of the Line Slicer are as follows:
+            <ol>
+                <li><span class="label">Slices list</span>: This lists the slices of data in the order they are stacked.</li>
+                <li><span class="label">Slice Properties</span>:This displays the properties for the selected Slice, normally controlling how the data is grouped.</li>
+                <li><span class="label">Data</span>: This graph constantly updates to give you a graphical representation of the data slices and groups you've set up.</li>
+            </ol>
+            </p>
+        </section>
+        <section class="card">
+            <h1>How It Works</h1>
+            To start, you must add a type of data to slice up. Most common case conditions (ex. Target, Also Playing, Said Marker, etc.) are available for selection to slice.<br/>
+            When multiple slices are added, it will branch out like a tree, performing subsequent slices on each group from the previous slice. In the screenshot above, there are two slices: Case Type and Also Playing.<br/>
+            Case Type has two groups: Self Must Strip, which is a user-defined group containing the four Must Strip (Self) case types, and Other is a default group for every other case type that isn't in another group. Also Playing has two groups as well: a group for cases that target Also Playing: Catria, and a group that targets Also Playing: someone else.
+            <br/>
+            When building the graph, it first slices the case type (the first slice in the list), dividing dialogue lines into 17 for the Self Must Strip group, and 150 lines for Other.<br/>
+            Next, it deals with each of those buckets separately for the next slice (Also Playing). The 17 must strip lines are further divided into 4 involving Catria, and 13 involving someone else. The 150 other lines are then divided into 59 involving Catria and 91 involving someone else.
+        </section>
+        <section class="card">
+            <h1>Filters and Grouping</h1>
+            You can filter out which lines you're actually interested in for a slice by clicking on a slice in the Slices list (section 1 of the image above). This will open its properties in section 2. Most slice types will prepopulate some sensible defaults. Working with groups varies based on the data type, but the general operations are as follows:
+            <ul>
+                <li>Use the search field to add a group.</li>
+                <li>Toggle a checkbox to hide that group from the graph.</li>
+                <li>Click the dot to combine two groups into one, or to split a combined group back into its individual components.</li>
+                <li>Click the pencil to rename a group.</li>
+                <li>Click the X to completely delete a group.</li>
+                <li>For ranges, a slider appears at the top of the properties. Right click on the slider to divide the range in two. Right click a split point to delete it and combine adjacent intervals.</li>
+            </ul>
+        </section>
+    </article>
+</body>
+</html>
diff --git a/editor source/SPNATI Character Editor/Help/metadata.html b/editor source/SPNATI Character Editor/Help/metadata.html
index 331d984728c0e0e3856fe28b53838e91fc222852..51779b0283ff1e32df23981835c9af92fe59e61a 100644
--- a/editor source/SPNATI Character Editor/Help/metadata.html	
+++ b/editor source/SPNATI Character Editor/Help/metadata.html	
@@ -15,32 +15,46 @@
     <article id="main">
         <section class="card" id="overview">
             <h1>Overview</h1>
-            Metadata defines characteristics about the character, which is used for determining how other characters will react to yours, as well as the data that shows on the player selection screen.
+            Metadata defines characteristics about the character that will assist players in finding your character on the selection screen. This includes their name and selection screen image along with information for the filters, such as their size, gender, tags, and so forth.
         </section>
         <section class="card" id="data">
-            <h1>Fields</h1>
+            <h1>Demographics</h1>
             <ul>
-                <li><span class="label">Label:</span> This is the name other characters will use when referring to your character.</li>
+                <li><span class="label">Label:</span> This is the name other characters will use when referring to your character. This can be changed in the middle of the game.</li>
                 <li><span class="label">First name:</span> Character's first name during player selection. Not used anywhere else.</li>
                 <li><span class="label">Last name:</span> Character's last name during player selection. Not used anywhere else.</li>
-                <li><span class="label">Default portrait:</span> Which image to use on the player selection screen.</li>
                 <li><span class="label">Gender:</span> Character's gender. Characters react differently based on gender.</li>
-                <li><span class="label">Size:</span> For males, this is the size of their genitals. For females, this is the size of their chest. Characters react differently based on size.</li>
-                <li><span class="label">Rounds to finish:</span> After a player has lost a hand while naked, they must masturbate. This value controls how many times the player has to click buttons before the character "finishes."</li>
+                <li><span class="label">Chest/Crotch (depends on gender):</span>  For males, this is the size of their genitals. For females, this is the size of their chest. Characters react differently based on size.</li>
+                <li><span class="label">Portrait:</span> Which image to use on the player selection screen.</li>
+                <li><span class="label">Height:</span> How tall the character is. This data is currently useless.</li>
+                <li><span class="label">Description:</span> Character bio used in the character selection screen.</li>
+                <li><span class="label">Origin:</span> Source material (game, anime, movie, etc.)</li>
+            </ul>
+            <h1>Credits</h1>
+            <ul>
                 <li><span class="label">Writer:</span> Who wrote the dialogue for the character</li>
                 <li><span class="label">Artist:</span> Who made the art for the character</li>
-                <li><span class="label">Origin:</span> Source material (game, anime, movie, etc.)</li>
-                <li><span class="label">Height:</span> How tall the character is. This data is currently useless.</li>
+            </ul>
+            <h1>Credits</h1>
+            <ul>
                 <li>
-                    <span class="label">Intelligence:</span> Determines how well the character plays. Bad AI will throw away random cards. Good AI will try to make the best moves.
+                    <span class="label">Intelligence:</span> Determines how well the character plays.
                     For player enjoyment's sake, you should try to balance AI and number of clothing layers. Bad AI with too few layers will lose too quickly too often, and good AI with too many layers will be frustrating to try to beat.
                     <br />You can specify different AI levels per character stage. A level will be used until the next listed stage is reached. For example, if you have two rows, 0-bad and 4-good, then your character will start with bad AI, but at stage 4, they will start using good AI.
+                    <ul>
+                        <li><span class="label">throw:</span> The character purposely tries to lose.</li>
+                        <li><span class="label">bad:</span> The character randomly trades cards.</li>
+                        <li><span class="label">average:</span> The character makes a decent effort to build a good hand. The majority of characters use this level.</li>
+                        <li><span class="label">good:</span> The character goes for what's most probable to win a hand (without resorting to cheating).</li>
+                        <li><span class="label">best:</span> Very similar to good, but slightly better.</li>
+                    </ul>
+                <li><span class="label">Rounds to finish:</span> After a player has lost a hand while naked, they must masturbate. This value controls how many times the player has to click buttons before the character "finishes."</li>
                 </li>
-                <li><span class="label">Description:</span> Character bio used in the character selection screen.</li>
+
             </ul>
         </section>
         <section class="card navCard" id="next">
-            <a href="creation.html">« 1. Getting Started</a> | <a href="tags.html">3. Tags »</a>
+            <a href="creation.html">« Getting Started</a> | <a href="wardrobe.html"> Wardrobe »</a>
         </section>
     </article>
 </body>
diff --git a/editor source/SPNATI Character Editor/Help/overview.html b/editor source/SPNATI Character Editor/Help/overview.html
index d2d6e2a1bd8c965c4776f793855dd041338756a6..4097ddf8ed989f4fddcc1471360afed888cc54d9 100644
--- a/editor source/SPNATI Character Editor/Help/overview.html	
+++ b/editor source/SPNATI Character Editor/Help/overview.html	
@@ -9,13 +9,6 @@
 </head>
 <body>
     <header id="header">Overview</header>
-    <ul id="nav">
-        <li><a href="#basics">Basics</a></li>
-        <li><a href="#metadata">Metadata</a></li>
-        <li><a href="#behaviour">Behaviour</a></li>
-        <li><a href="#images">Images</a></li>
-        <li><a href="#source">Source files</a></li>
-    </ul>
     <article id="main">
         <section class="card" id="basics">
             <h1>Basics</h1>
@@ -23,33 +16,74 @@
             <p>Use the navigation pane on the left to dig into particular topics.</p>
             <p>Each character has a dedicated folder inside the opponents directory, which contains all the data to make a character work.</p>
             <p>Additionally, each character has an entry in listing.xml found in the opponents directory. This is how the game actually recognizes a character is available for play. 
-                You do not need to worry about this file. When you are ready to submit your character, a mod will take care of it for you.</p>
+                You do not need to worry about this file. When you are ready to submit your character, a mod will take care of it for you. In the meantime, the editor will handle adding your character for testing purposes.</p>
             <p>The sections below describe the various files found within your character's folder in the opponents directory.</p>
         </section>
-        <section class="card" id="metadata">
-            <h1>meta.xml</h1>
-            This file contains your character's name, gender, and various metadata about them: the artist and writer, character bio, original source material, etc.
-            <p>This file is rebuilt from the Metadata tab whenever you save.</p>
-        </section>
-        <section class="card" id="behaviour">
-            <h1>behaviour.xml</h1>
-            This is the meat of your character, containing all of their dialogue, the order they remove their clothing, what poses to use for each line, when to use certain dialogue, etc.
-            Epilogue information is also found in this file.
-            <p>This file's data is spread across the editor's various tabs.</p>
-        </section>
-        <section class="card" id="images">
-            <h1>Images</h1>
-            Numerous images are located in the folder. These typically come in the format <i>&lt;number&gt;-&lt;emotion&gt;.png</i>, representing the pose for a particular emotion during a particular stage.
-            <p>Ending images are also stored here.</p>
+        <section class="card" id="folder">
+            <h1>Character Folder Composition</h1>
+            <p>All the information to make your character work is located in their folder. When you first create a character, you will be asked what folder to create. This is where all information about your character will be stored.</p>
+            <p>There are several types of files located in this folder, most of which will be generated by the editor automatically.</p>
+            <ul>
+                <li><span class="label">meta.xml</span> This contains basic information about your character used by the game's character selection screen: their name, authorship, epilogue availability, etc. This file exists so that the game does not need to load a character's entire dialogue file until they are actually selected for playing, speeding up initial load times.</li>
+                <li><span class="label">behaviour.xml</span> This is meat of your character, containing all of their dialogue, the order they remove their clothing, what poses to use for each line, when to use certain dialogue, etc. Basically everything after the game has begun.</li>
+                <li><span class="label">collectibles.xml</span> This contains information about the unlockable collectibles associated with your character. See the Collectibles section for more information.</li>
+                <li><span class="label">editor.xml</span> This contains special information used by the Character Editor related to your character. The game does not use this file.</li>
+                <li><span class="label">styles.cs</span> This is created automatically when adding custom text formatting in the Advanced tab. Your character's custom styles are stored in this file.</li>
+                <li><span class="label">Images</span> Images will comprise the bulk of your files. These come in three main flavors:
+                    <ol>
+                        <li>Game poses: Usually in the format <i>&lt;number&gt;-&lt;emotion&gt;.png</i>, these are images directly associated with dialogue lines in game.</li>
+                        <li>Pose Maker assets: Raw images used for sprites when creating custom poses with the Pose Maker. These have no special naming convention.</li>
+                        <li>Epilogue assets: Raw images used for sprites and backgrounds used in epilogues. These have no special naming convention.</li>
+                    </ol>
+                </li>
+                <li><span class="label">Miscellaneous</span> Other files may appear depending on which editor features you use. None of these are used by the game itself.
+                    <ul>
+                        <li>edit-dialogue.txt: Used if you export your character to a text file for editing outside the editor.</li>
+                        <li>Pose data (ex. poses.txt): Files containing pose lists of exported Kisekae codes with image names. These are used to generate images from Kisekae models.</li>
+                    </ul>
+                </li>
+            </ul>
         </section>
-        <section class="card" id="source">
-            <h1>Source files</h1>
-            Everything else in the folder is not used by the game, but are just source files used to help create the files the game uses.
-            Common files include:
+        <section class="card" id="screen">
+            <h1>Screen Layout</h1>
+            The screen is divided into tabbed workspaces allowing for having multiple characters open at once within the same window. Following is an overview of all the basic components that comprise the Character Editor:
+            <img src="images/layout.png" />
             <ul>
-                <li>Dialogue (*.txt): A file containing raw dialogue information, commonly used for creating data prior to the creation of the Character Editor (via the make_xml.py script). The Editor is capable of reading and writing these files.</li>
-                <li>Pose data (*.txt): A file associating Kisekae codes with image names, used to auto-create image files as described in the Images section. The Editor is capable of reading and writing these files.</li>
-                <li>Backup data (*.edit.bak): Auto-generated backups of meta.xml and behaviour.xml that the Character Editor creates periodically. These do not need to be included when submitting your character for use.</li>
+                <li><span class="label">1. Main toolbar:</span> Contains system level actions and access to various workspaces. 
+                    <ul>
+                        File: Options for creating, saving, importing and exporting characters.
+                        <li>Edit: Options for bringing up Find &amp; Replace. This is only functional while in the Dialogue tab.</li>
+                        <li>Characters...: This brings up a search prompt for opening a character's workspace.
+                            <div class="info">
+                                <h5><i class="fa fa-info"></i>Why isn't my character showing up?</h5>
+                                If your character isn't in the list, one of their xml files is likely invalid. Check errorlog.txt for more details. You should never run into this issue when editing characters exclusively in the editor, but this can happen if people manually edit the xml.
+                            </div>
+                        </li>
+                        <li>Skins...: This brings up a search prompt for opening a reskin (alternate outfit)'s workspace.</li>
+                        <li>Validate: Opens the validator for validating all characters in the game. Most users are going to find more value in the character-specific validator found in the character workspace over this.</li>
+                        <li>
+                            Tools: Various various tools for reporting and editor preferences:
+                            <ul>
+                                <li>Charts: Provides several graphs for reporting on various aspects across the entire character roster.</li>
+                                <li>Marker Report: Provides a report for seeing how and where each character uses markers.</li>
+                                <li>Data Analyzer: Provides high level reporting for sifting through the roster based on one or more criteria (ex. reporting which characters have an epilogue but fewer than 1000 lines of dialogue)</li>
+                                <li>Configure Game: Provides settings for the offline game, such as enabling cheat mode, unlocking all epilogues, etc.</li>
+                                <li>Manage Macros: Allows creating, editing, or deleting user macros. Refer to the Macros section for more information.</li>
+                                <li>Manage Dictionary: Allows for modifying your user dictionary used with Spell Check.</li>
+                                <li>Manage Recipes: Allows for creating, editing, or deleting user recipes. Refer to the Recipes section for more information.</li>
+                                <li>Data Recovery: If the editor has auto-backups enabled, this tool will let you jump back in time, but only within the last few days. Regular commits to Git branch are the preferred tool for backing up your information.</li>
+                                <li>Fix Kisekae: Some complicated KKL codes can cause the import mechanism to choke. This tool might help fix it if imports stop working.</li>
+                            </ul>
+                        </li>
+                        <li>Help: Where you found this resource. You can also look at editor change logs or identify your editor version number here.</li>
+                    </ul>
+                </li>
+                <li><span class="label">2. Action toolbar:</span> Quick menus for opening setup, changing your theme, or quitting the application.</li>
+                <li><span class="label">3. Workspace tabs:</span> When opening a character, a tab for that character will be created here. Other things like tools will also create tabs. These tabs are the access point to a character's "workspace", the working area specifically related to that character. Everything beneath this tab strip composes the active tab's workspace.</li>
+                <li><span class="label">4. Activity tabs:</span> Workspaces are split into one or more "activities" that serve as a way to organize the different parts of a workspace. For instance, the character workspace has tabs for writing dialogue, making epilogues, importing poses, and so on.</li>
+                <li><span class="label">5. Main pane:</span> The active activity appears in this area.</li>
+                <li><span class="label">6. Sidebar</span> This part of the workspace is not tied to the activity tabs and persists when switching between them. Generally this contains a preview image pertinent to whatever data you're looking at in the main pane, such as the pose attached to a line of dialogue you're writing.</li>
+                <li><span class="label">7. Status bar</span> Messages appear down here from time to time reporting on the completion of tasks where a popup would be annoying, such as a report when an auto-save occurs.</li>
             </ul>
         </section>
     </article>
diff --git a/editor source/SPNATI Character Editor/Help/poses.html b/editor source/SPNATI Character Editor/Help/poses.html
index 5297e09e119147ca09b48dbd58bb8cbf99516c5b..e413d174176ecf424e302bd3325fe644b4a178fc 100644
--- a/editor source/SPNATI Character Editor/Help/poses.html	
+++ b/editor source/SPNATI Character Editor/Help/poses.html	
@@ -21,14 +21,29 @@
         </section>
         <section class="card" id="basics">
             <h1>Basics</h1>
-            Character poses are generated using the offline version of Kisekae found in the tools folder of the SPNATI repository you downloaded. This guide will not include details about creating characters. There are many resources online.
+            <p>
+                Stripping away (ha!) all the game's fancy whistles, at its heart a character consists of a bunch of lines of dialogue, where each line contains some text and a corresponding pose.
+                Before you jump into writing dialogue, it's generally useful to at least have a few poses at your disposal, because looking at your character's reactions can inform your writing.
+            </p>
+            <p>
+                Poses can either be image files (.PNG or .GIF) or a real-time animation created by the Pose Maker that is built from one or more image files. Images use a specific naming convention <strong>#-emotion</strong> where <strong>#</strong> is the stage the image belongs to, and <strong>emotion</strong> is the name of the pose.
+            </p>
+            <p>
+                In order to maintain a consistent art style across characters, we use a paper doll tool known as Kisekae / KKL for generating the PNGs used by the game. You can find this in the tools folder of the SPNATI repository that you downloaded. This guide will not include details about creating models. There are many resources online, and a dedicated Discord channel for using KKL with SPNATI.
+            </p>
+
+            <div class="info">
+                <h5><i class="fa fa-info"></i>Remember, SPNATI does <b>not</b> use KKL.</h5>
+                KKL is merely the tool used for producing the image files that SPNATI <em>does</em> use. The game does not know or care  where an image originated from. Indeed, many characters are touched up in Photoshop after being created in KKL.
+            </div>
+
             <p>Your primary friend when using Kisekae to interact with the editor is the Export button found in the file menu:</p>
             <img src="images/export.png" alt="Kisekae export button" />
-            <p>This will display a textbox containing a bunch of gibberish code. You will be copying this code and pasting it into the relevant fields in the Images tab of the editor.</p>
+            <p>This will display a textbox containing a bunch of gibberish code. You will be copying this code and pasting it into the relevant fields in the Poses tab of the editor.</p>
             <p>You can filter out pieces of the scene by clicking the buttons at the bottom of the Export window. This is important when generating templates, but for simply getting an image generated, you typically want everything selected.</p>
-            <p>Kisekae codes are essentially the entire character information (face, clothing, pose, etc.) compressed into a text format. The editor can in turn take these codes and convert them into PNG files.</p>
-            <p>An example code:</p>
-            <pre style="word-break: break-all; white-space: pre-wrap;">54***0*0*0*aa8.99.1.0.59.8.0.1.0.59_ab_ac_ba46_bb6.1_bc440.500.7.0.1.0_bd6_be180_ca63.1.16.63.29.18.31.18.31.1.60.80_daF0CDC8.0.0.100_db_dd9.3.21.0.0.29_dhE08781.20.50.43.3_di5_qa_qb_dc7.4.F8D2D2.D2948E.E6826C_eh_ea2.32.32.56.0.0_ec7.29.32.32.56.42.63.1_ed15.12.1.1.32.56_ef_eg_r00_fa1.65.53.49.16.50.56_fb_fh_fc1.32.55.1.32.55.42.61.61.42.50.50_fj0.0.0_fd2.0.31.32.5_fe50.77_ff0000000000_fg8.63.56.0.0.1.0.0_fi4.70.20.30.30_pa0.0.0.0.10.50.85.78.0.0_t00_pb_pc_pd_pe_ga0_gb0_gc3.0_ge0000000000_gh_gf_gg_gd10000000_ha100.100_hb53.1.50.100_hc0.50.48.0.50.48_hd0.1.50.50_ia_if_ib_id_ic_jc_ie_ja_jb_jd_je_jf_jg_ka_kb_kc_kd_ke_kf_la_lb_oa_os_ob_oc_od_oe_of_lc_m00_n00_s00_og_oh_oo_op_oq_or_om_on_ok_ol_oi_oj_ad0.0.0.0.0.0.0.0.0.0*0*0*0*0*0</pre>
+            <p>Kisekae codes are essentially the entire model information (face, clothing, pose, etc.) encoded into a text format. The editor can in turn take these codes and convert them into PNG files (or rather, tells Kisekae to convert them).</p>
+            <p>An example code for SPNATI-chan, the SPNATI Character Editor mascot:</p>
+            <pre class="code">86**aa13.84.0.0.59.8.82.0.14.59_ab_ac_ba68_bb10.1_bc410.500.8.0.1.0_bd2_be189_bf_bg_bh1_ca63.1.16.63.0.9.31.18.31.1.60.80_daf7dcd0.0.25.100_db_dd9.1.32.-30.10.17_dheb9e8c.20.50.43.3_di6_qa_qb_dc5.4.f7dcd0.f9c3c7.e6826c.0_eh_ea2.6.6.56.0.0_ec6.29.6.6.56.42.63.1_ed17.22.1.1.6.56_ef0.2.22.6.6.56.0.1_eg0.2.22.6.6.56.0.1_r00_fa15.54.65.54.23.36.56_fb_fh_fc1.32.55.1.32.55.42.61.61.42.50.50_fj0.0.0_fd2.0.20.5.5_fe48.82_ff0000010000_fg1.45.56.50.56.1.0.0_fi1.71.18.25.25_pa0.55.8.51.10.50.85.78.0.0_t00_pb_pc_pd_pe_ga0_gb0.0.10.40.65_gc10.0_ge0000000000_gh0_gf_gg_gd000000_ha100.100_hb50.1.50.50.50.50.50_hc0.50.48.0.50.48_hd0.1.50.50.2.60.50.50_ad0.0.0.0.0.0.0.0.0.0_ae1.0.0.0.0_ia_if10.c3ffe4.3f253c.973636.1.12.b2e9d0.55.12.b2e9d0.55.1.1.1.0.0.0.0.1_ib_id10.55.3f1e4b.3f1e4b.0.0.1.3.02218b.55.3.02218b.55.2.0.0.0.0.1_ic32.260134.3f1e4b.260134.0_jc_ie4.56.51.48.19.1b0c23.43.0.19.1b0c23.43.0.0_ja_jb_jf_jg_jd11.55.3f1e4b.55.4.6e5e63.58.59_je11.55.3f1e4b.55.4.6e5e63.58.59_ka2.14.14.14.0_kb2.14.14.14_kc_kd_ke_kf_la_lb_oa_os_ob_oc_od_oe_of_lc_m004.1b0c23.2.42.1.2.-15.28.280.99.1.61.0.500.0.2.1_m014.1b0c23.2.42.2.2.-15.17.862.136.1.61.0.500.0.2.1_n00_s150.55.55.43.0.0.144.604.911.1.11.0.25.0.61.500_og_oh_oo_op_oq_or_om_on_ok_ol_oi_oj_f00</pre>
         </section>
         <section class="card" id="template">
             <h1>Templates</h1>
@@ -69,7 +84,7 @@
         </section>
         <section class="card" id="poseList">
             <h1>Pose Lists</h1>
-            If you choose to do each pose manually, or want to convert a specific code into an image, or tweak something the template created, you would do it on the Poses tab.
+            If you choose to do each pose manually, or want to convert a specific code into an image, or tweak something in the list the template created, you would do it on the Poses tab.
             A pose list contains the Kisekae codes typically corresponding to the images in the character's folder. Images can also be split among several pose lists.
             <div class="info">
                 <i class="fa fa-info"></i>
@@ -86,6 +101,7 @@
                         The editor and make_images.py use different importing techniques, including how cropping is defined. Therefore, it's best to always import using the same tool, either the Python script or the Character Editor, and not mix them.
                     </div>
                 </li>
+                <li><span class="label">Metadata:</span> The more button brings up pose metadata, where you can control part transparencies (ex. hide an arm or make a skirt partially transparent), or turn off the editor's pre-processors for a pose.</li>
                 <li><span class="label">Code:</span> The complete Kisekae export code for the pose. This can be imported back into Kisekae to modify the pose, and then reexported.</li>
                 <li><span class="label">Image:</span> If the file for this pose exists, its preview will be displayed here. If the image has never been generated, this will be blank.</li>
             </ul>
@@ -143,7 +159,7 @@
             </ol>
         </section>
         <section class="card navCard" id="next">
-            <a href="wardrobe.html">« 4. Wardrobe</a> | <a href="dialogue.html">5. Dialogue »</a>
+            <a href="tags.html">« Wardrobe</a> | <a href="dialogue.html">Dialogue »</a>
         </section>
     </article>
 </body>
diff --git a/editor source/SPNATI Character Editor/Help/tags.html b/editor source/SPNATI Character Editor/Help/tags.html
index 087f0b2f1614f4521388411288d32bb76fe4cda4..60b338e50458a07aad57489b8659803cedc62b62 100644
--- a/editor source/SPNATI Character Editor/Help/tags.html	
+++ b/editor source/SPNATI Character Editor/Help/tags.html	
@@ -4,7 +4,7 @@
 <head>
     <meta charset='utf-8' />
     <link rel="stylesheet" type="text/css" href="topic.css" />
-    <title>Metadata</title>
+    <title>Tags</title>
 </head>
 <body>
     <header id="header">Tags</header>
@@ -15,10 +15,15 @@
         <section class="card" id="overview">
             <h1>Overview</h1>
             Tags are adjectives that define characteristics about your character. These allow other characters to target dialogue towards your character without targeting your character specifically.
-            Check everything that applies to your character. If there is something unique that doesn't have a tag already, you can add them free-text in the table, but this should only be a last resort, as it's unlikely anyone will ever target them.
+            <br/>
+            On the left is a list of categories, the middle a grid for the selected category, and the right an overview of all tags currently applied.
+            <br/>
+            Tags can be added or removed mid-game based on what stage the character is in. For each category, fill out the stages that a particular tag belongs in. If a tag is used all game (for instance, your character has black hair from beginning to end), you can use the "Select All" column to auto-check the entire row. For tags that don't apply all game (for instance, your character has a hat but loses it after three rounds), check only those columns where the tag applies.
+            <br/>
+            If there is something unique that doesn't have a tag already, you can add them free-text in the table, but this should only be a last resort, as it's unlikely anyone will ever target something that isn't an official tag.
         </section>
         <section class="card navCard" id="next">
-            <a href="creation.html">« 1. Getting Started</a> | <a href="wardrobe.html">3. Wardrobe »</a>
+            <a href="metadata.html">« Wardrobe</a> | <a href="poses.html"> Poses »</a>
         </section>
     </article>
 </body>
diff --git a/editor source/SPNATI Character Editor/Help/wardrobe.html b/editor source/SPNATI Character Editor/Help/wardrobe.html
index 96bb53bfff9b949f1190b838d12730ae7df21392..bf6bd718377b7523156c5707448549654df0836c 100644
--- a/editor source/SPNATI Character Editor/Help/wardrobe.html	
+++ b/editor source/SPNATI Character Editor/Help/wardrobe.html	
@@ -19,30 +19,29 @@
             <h1>Overview</h1>
             Your character will strip in a pre-determined order, which never changes from game to game. The Wardrobe tab is where you define this order.<br />
             Each layer of clothing contains extra information about that article that other players will react to - what it covers, how much it covers, etc.<br />
-            <p>This screen lists out the layers of clothing your character has, ordered in the same order that your character will remove them in.</p>
+            <p>This screen lists out the layers of clothing your character has, ordered in the same order that your character will remove them.</p>
             To begin, simply start filling out the first line of the grid with the first article that will be removed.
         </section>
         <section class="card" id="layers">
             <h1>Setting Layers</h1>
             Strip order can be controlled using layers.
             <ul>
-                <li><span class="label">Adding a Layer:</span> Start typing into the last row of the grid to add a new layer. There is technically no limit to the number of layers, but no existing character has more than 8, and the editor will not appreciate trying to add more than that.</li>
+                <li><span class="label">Adding a Layer:</span> Start typing into the last row of the grid to add a new layer. There is technically no limit to the number of layers, but no existing character has more than 8, and the editor will really not appreciate trying to add more than that.</li>
                 <li><span class="label">Removing a Layer:</span> Click the open space to the left of a layer and press Delete to remove it.</li>
                 <li><span class="label">Reordering a Layer:</span> Use the up and down arrows to move a layer around in the strip order.</li>
             </ul>
             <div class="warning">
                 <h5><i class="fa fa-warning"></i>Be careful changing layers after you've already written dialogue.</h5>
                 Rearranging layers will also rearrange that layer's (stage's) dialogue. While you won't lose any lines (unless you delete a layer, of course), 
-                it can be a real pain to update dialogue to reflect the new strip order.
+                it can be a real pain to update dialogue to reflect the new strip order. If your character is already released, it will be a massive pain for any opponents targeting the old stripping layer. For this reason, many options in the Wardrobe tab are locked down once a character leaves testing. If you really want to change things, you will need to do it manually outside the editor.
             </div>
         </section>
         <section class="card" id="data">
             <h1>Layer Data</h1>
             Each layer has a few relevant pieces of information.
             <ul>
-                <li><span class="label">Proper Name:</span> Capitalized name of the piece of clothing.</li>
-                <li><span class="label">Lowercase Name:</span> Lowercase version of the name.</li>
-                <li><span class="label">Plural:</span> Check this if the clothing is plural (ex. pants, socks, anything where one would say, "Your X are" instead of "Your X is"). This helps dialogue writers use proper grammar when reacting to your clothing.</li>
+                <li><span class="label">Name (lowercase):</span> What people in the game will call the article of clothing. This should be lowercase. The game will capitalize it automatically where necessary.</li>
+                <li><span class="label">Is Plural:</span> Check this if the clothing is plural (ex. pants, socks, anything where one would say, "Your X are" instead of "Your X is"). This helps dialogue writers use proper grammar when reacting to your clothing.</li>
                 <li><span class="label">Type:</span> How much the clothing covers. Stripping reactions are based on this type.
                     <ul>
                         <li><span class="label">extra: </span>Accessories or other minor, non-clothing articles.</li>
@@ -51,17 +50,17 @@
                         <li><span class="label">important: </span>Clothing that covers a vital body part (chest or genitals). Typically underwear.</li>
                     </ul>
                 </li>
-                <li><span class="label">Position:</span> Where the clothing is located. This is only used for determining if an important layer is revealing a character's crotch or chest.
+                <li><span class="label">Position:</span> Where the clothing is located. This is both used for determining if an important layer is revealing a character's crotch or chest, and for advanced targeting such as a character recognizing if something was removed from the feet regardless of what that something is called.
                     <ul>
                         <li><span class="label">upper: </span>Covers the character's chest</li>
                         <li><span class="label">lower: </span>Covers the character's crotch</li>
-                        <li><span class="label">other: </span>Covers some other part of the character</li>
+                        <li><span class="label">other: </span>Other options have no functional impact, but try to make them as accurate as possible for targeting purposes.</li>
                     </ul>
                 </li>
             </ul>
         </section>
         <section class="card navCard" id="next">
-            <a href="tags.html">« 3. Tags</a> | <a href="poses.html">5. Importing Poses »</a>
+            <a href="tags.html">« Metadata</a> | <a href="tags.html">Tags »</a>
         </section>
     </article>
 </body>
diff --git a/editor source/SPNATI Character Editor/HelpForm.Designer.cs b/editor source/SPNATI Character Editor/HelpForm.Designer.cs
index 4afa90b7464fe7cf7efdd43315b1a7173634832d..98d8edef7c4770e1b3bacd3187fcea4b1693442a 100644
--- a/editor source/SPNATI Character Editor/HelpForm.Designer.cs	
+++ b/editor source/SPNATI Character Editor/HelpForm.Designer.cs	
@@ -33,11 +33,13 @@
 			// 
 			// wb
 			// 
-			this.wb.Dock = System.Windows.Forms.DockStyle.Fill;
-			this.wb.Location = new System.Drawing.Point(0, 0);
+			this.wb.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+			this.wb.Location = new System.Drawing.Point(1, 28);
 			this.wb.MinimumSize = new System.Drawing.Size(20, 20);
 			this.wb.Name = "wb";
-			this.wb.Size = new System.Drawing.Size(1226, 702);
+			this.wb.Size = new System.Drawing.Size(1223, 673);
 			this.wb.TabIndex = 0;
 			this.wb.Url = new System.Uri("", System.UriKind.Relative);
 			// 
diff --git a/editor source/SPNATI Character Editor/HelpForm.cs b/editor source/SPNATI Character Editor/HelpForm.cs
index ca768132a85cb938769498042900bacd79ff9f1a..c1854e6a48f8d5cf78c021df6e38ed096fb2f44b 100644
--- a/editor source/SPNATI Character Editor/HelpForm.cs	
+++ b/editor source/SPNATI Character Editor/HelpForm.cs	
@@ -1,10 +1,10 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.IO;
-using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor
 {
-	public partial class HelpForm : Form
+	public partial class HelpForm : SkinnedForm
 	{
 		public HelpForm()
 		{
diff --git a/editor source/SPNATI Character Editor/Hunspellx64.dll b/editor source/SPNATI Character Editor/Hunspellx64.dll
new file mode 100644
index 0000000000000000000000000000000000000000..55a29ec845706fdf1825320b9a31ae496b673975
Binary files /dev/null and b/editor source/SPNATI Character Editor/Hunspellx64.dll differ
diff --git a/editor source/SPNATI Character Editor/Hunspellx86.dll b/editor source/SPNATI Character Editor/Hunspellx86.dll
new file mode 100644
index 0000000000000000000000000000000000000000..7bc6cf22240fcefad1ded5d0a865827ff0045db5
Binary files /dev/null and b/editor source/SPNATI Character Editor/Hunspellx86.dll differ
diff --git a/editor source/SPNATI Character Editor/IO/CharacterStyleSheetSerializer.cs b/editor source/SPNATI Character Editor/IO/CharacterStyleSheetSerializer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b952ff7828b3a0e51325886bcbc64e41fa60fd49
--- /dev/null
+++ b/editor source/SPNATI Character Editor/IO/CharacterStyleSheetSerializer.cs	
@@ -0,0 +1,168 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace SPNATI_Character_Editor.IO
+{
+	public static class CharacterStyleSheetSerializer
+	{
+		public static CharacterStyleSheet Load(Character character, string sheetName)
+		{
+			string path = Path.Combine(character.GetDirectory(), sheetName);
+			if (!File.Exists(path))
+			{
+				CharacterStyleSheet defaultSheet = new CharacterStyleSheet();
+				defaultSheet.Name = "styles.css";
+
+				StyleRule selector = new StyleRule();
+				selector.ClassName = "example";
+				selector.Attributes.Add(new StyleAttribute("color", "blue"));
+				selector.Description = "This is an example style.";
+				defaultSheet.Rules.Add(selector);
+				return defaultSheet;
+			}
+
+			CharacterStyleSheet sheet = new CharacterStyleSheet();
+			sheet.Name = sheetName;
+
+			//I don't feel like making a robust CSS parser, so this is only going for the very basics
+			bool requireAdvanced = false;
+			try
+			{
+				string[] lines = File.ReadAllLines(path);
+				StringBuilder working = new StringBuilder();
+				StyleRule rule = null;
+				foreach (string line in lines)
+				{
+					string text = line.Trim();
+					if (string.IsNullOrEmpty(text))
+					{
+						continue;
+					}
+					if (rule == null)
+					{
+						if (text.Length > 1 && text.EndsWith("{"))
+						{
+							working.Append(text.Substring(0, text.Length - 1));
+						}
+						if (text.EndsWith("{"))
+						{
+							//parse out the relevant class name
+							string selectorText = working.ToString().Trim();
+							Regex regex = new Regex(@"\.dialogue \.(\w+)\[data-character=""\w+""\]");
+							Match match = regex.Match(selectorText);
+							if (match.Success)
+							{
+								string className = match.Groups[1].Value;
+								rule = new StyleRule();
+								rule.ClassName = className;
+								sheet.Rules.Add(rule);
+								working.Clear();
+							}
+							else
+							{
+								requireAdvanced = true;
+								break;
+							}
+						}
+						else
+						{
+							working.Append(text);
+						}
+					}
+					else
+					{
+						//building the rule
+						if (text == "}")
+						{
+							working.Clear();
+							rule = null;
+						}
+						else if (text.StartsWith("/*"))
+						{
+							//description
+							rule.Description = CssDecode(text.Replace("/*", "").Replace("*/", ""));
+						}
+						else
+						{
+							string style = text;
+							if (text.EndsWith("}") && text.Length > 1)
+							{
+								style = text.Substring(0, text.Length - 1);
+							}
+							string[] pieces = text.Split(new char[] { ':' }, 2);
+							if (pieces.Length == 2)
+							{
+								string attribute = pieces[0].Trim();
+								string value = pieces[1].Trim(new char[] { ' ', ';' });
+								StyleAttribute attr = new StyleAttribute(attribute, value);
+								rule.Attributes.Add(attr);
+							}
+							else
+							{
+								requireAdvanced = true;
+								break;
+							}
+							if (text.EndsWith("}"))
+							{
+								working.Clear();
+								rule = null;
+							}
+						}
+					}
+				}
+
+
+				if (requireAdvanced)
+				{
+					//we're too dumb to understand this file. Just store the whole thing and force advanced mode
+					sheet.Rules.Clear();
+					sheet.FullText = File.ReadAllText(path);
+					sheet.AdvancedMode = true;
+				}
+			}
+			catch { }
+
+			return sheet;
+		}
+
+		public static void Save(Character character, CharacterStyleSheet sheet)
+		{
+			if (sheet == null) { return; }
+			string path = Path.Combine(character.GetDirectory(), sheet.Name);
+			List<string> selectors = new List<string>();
+			if (sheet.AdvancedMode)
+			{
+				File.WriteAllText(path, sheet.FullText);
+			}
+			else
+			{
+				string id = CharacterDatabase.GetId(character);
+				foreach (StyleRule selector in sheet.Rules)
+				{
+					selectors.Add(selector.Serialize(id));
+				}
+				string output = string.Join("\r\n\r\n", selectors);
+				File.WriteAllText(path, output);
+			}
+		}
+
+		public static string CssEncode(string input)
+		{
+			input = input.Replace("{", "\\00007B");
+			input = input.Replace("}", "\\00007D");
+			input = input.Replace("*", "\\00002A");
+			return input;
+		}
+
+		public static string CssDecode(string input)
+		{
+			//AntiXssEncoder lacks a CssDecode, so make up a cheapo one
+			input = input.Replace("\\00007B", "{");
+			input = input.Replace("\\00007D", "}");
+			input = input.Replace("\\00002A", "*");
+			return input;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/IO/FlatFileSerializer.cs b/editor source/SPNATI Character Editor/IO/FlatFileSerializer.cs
index 6a71ae67103bd5f11ae03e27568f860b5375888c..7afff0f2907fada96650d397b2f4b456f8c3eb6f 100644
--- a/editor source/SPNATI Character Editor/IO/FlatFileSerializer.cs	
+++ b/editor source/SPNATI Character Editor/IO/FlatFileSerializer.cs	
@@ -1,10 +1,9 @@
-using SPNATI_Character_Editor.IO;
+using Desktop.CommonControls;
+using SPNATI_Character_Editor.IO;
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
-using System.Text;
 
 namespace SPNATI_Character_Editor
 {
@@ -64,6 +63,8 @@ namespace SPNATI_Character_Editor
 			lines.Add("writer=" + metadata.Writer);
 			lines.Add("artist=" + metadata.Artist);
 			lines.Add("description=" + metadata.Description);
+			lines.Add("z-layer=" + metadata.Z);
+			lines.Add("dialogue-layer=" + metadata.BubblePosition);
 
 			lines.Add("");
 			lines.Add("#When selecting the characters to play the game, the first line will always play, then it randomly picks from any of the start lines after you commence the game but before you deal the first hand.");
@@ -188,14 +189,17 @@ namespace SPNATI_Character_Editor
 				{
 					foreach (int stage in c.Stages)
 					{
-						Case stageCase = c.CopyConditions();
-						stageCase.Stages.Add(stage);
-						stageCase.Id = c.Id;
-						foreach (var line in c.Lines)
+						foreach (Case set in c.GetConditionSets())
 						{
-							stageCase.Lines.Add(Behaviour.CreateStageSpecificLine(line, stage, character));
+							Case stageCase = set.CopyConditions();
+							stageCase.Stages.Add(stage);
+							stageCase.Id = c.Id;
+							foreach (var line in c.Lines)
+							{
+								stageCase.Lines.Add(Behaviour.CreateStageSpecificLine(line, stage, character));
+							}
+							cases.Add(stageCase);
 						}
-						cases.Add(stageCase);
 					}
 				}
 			}
@@ -360,6 +364,10 @@ namespace SPNATI_Character_Editor
 					{
 						lineCode += $",persist-marker:1";
 					}
+					if (defaultLine.OneShotId > 0)
+					{
+						lineCode += $",one-shot-id:{defaultLine.OneShotId}";
+					}
 					if (defaultLine.Weight != 1)
 					{
 						lineCode += $",weight:{defaultLine.Weight.ToString(CultureInfo.InvariantCulture)}";
@@ -483,6 +491,10 @@ namespace SPNATI_Character_Editor
 					{
 						lines.Add($"\t\t\ttext={directive.Text}");
 					}
+					else if (!string.IsNullOrEmpty(directive.Title))
+					{
+						lines.Add($"\t\t\ttext={directive.Title}");
+					}
 
 					foreach (Keyframe keyframe in directive.Keyframes)
 					{
@@ -504,6 +516,31 @@ namespace SPNATI_Character_Editor
 						}
 						lines.Add($"\t\t\tkeyframe={string.Join(",", keyAttributes)}");
 					}
+
+					foreach (Choice choice in directive.Choices)
+					{
+						List<string> choiceAttributes = new List<string>();
+						ElementInformation choiceInfo = SpnatiXmlSerializer.GetSerializationInformation(typeof(Choice));
+						foreach (FieldInformation info in choiceInfo.Fields)
+						{
+							if (info.Attribute == null) { continue; }
+							string value = info.GetValue(choice)?.ToString();
+							if (info.FieldType == typeof(bool))
+							{
+								value = (bool)info.GetValue(choice) ? "1" : null;
+							}
+							if (string.IsNullOrEmpty(value))
+							{
+								continue;
+							}
+							choiceAttributes.Add($"{info.Attribute.AttributeName}:{value}");
+						}
+						lines.Add($"\t\t\tchoice={string.Join(",", choiceAttributes)}");
+						if (!string.IsNullOrEmpty(choice.Caption))
+						{
+							lines.Add($"\t\t\t\ttext={choice.Caption}");
+						}
+					}
 				}
 			}
 		}
@@ -725,6 +762,10 @@ namespace SPNATI_Character_Editor
 			{
 				filters.Add("hidden:1");
 			}
+			if (stageCase.OneShotId > 0)
+			{
+				filters.Add("oneShotId:" + stageCase.OneShotId);
+			}
 			if (!string.IsNullOrEmpty(stageCase.CustomPriority))
 			{
 				filters.Add("priority:" + stageCase.CustomPriority);
@@ -762,6 +803,7 @@ namespace SPNATI_Character_Editor
 			EndingText currentText = null;
 			Scene currentScene = null;
 			Directive currentDirective = null;
+			Choice currentChoice = null;
 			Pose currentPose = null;
 			PoseDirective currentPoseDirective = null;
 
@@ -858,6 +900,16 @@ namespace SPNATI_Character_Editor
 					case "description":
 						character.Metadata.Description = value;
 						break;
+					case "z-layer":
+						int layer;
+						if (int.TryParse(value, out layer))
+						{
+							character.Metadata.Z = layer;
+						}
+						break;
+					case "dialogue-layer":
+						Enum.TryParse(value, out character.Metadata.BubblePosition);
+						break;
 					case "start":
 						Case temp = MakeLine(key, value, character, true);
 						if (temp != null)
@@ -873,6 +925,7 @@ namespace SPNATI_Character_Editor
 						currentText = null;
 						currentScene = null;
 						currentDirective = null;
+						currentChoice = null;
 						currentPose = null;
 						currentPoseDirective = null;
 						character.Endings.Add(currentEnding);
@@ -899,9 +952,20 @@ namespace SPNATI_Character_Editor
 							currentText.Content = value;
 							currentScreen.Text.Add(currentText);
 						}
+						else if (currentChoice != null)
+						{
+							currentChoice.Caption = value;
+						}
 						else if (currentDirective != null)
 						{
-							currentDirective.Text = value;
+							if (currentDirective.DirectiveType == "prompt")
+							{
+								currentDirective.Title = value;
+							}
+							else
+							{
+								currentDirective.Text = value;
+							}
 						}
 						break;
 					case "x":
@@ -943,6 +1007,7 @@ namespace SPNATI_Character_Editor
 						{
 							currentScene = new Scene();
 							currentDirective = null;
+							currentChoice = null;
 							ParseAttributes(currentScene, value);
 							currentEnding.Scenes.Add(currentScene);
 						}
@@ -951,6 +1016,7 @@ namespace SPNATI_Character_Editor
 						if (currentScene != null)
 						{
 							currentDirective = new Directive();
+							currentChoice = null;
 							ParseAttributes(currentDirective, value);
 							currentScene.Directives.Add(currentDirective);
 						}
@@ -975,10 +1041,20 @@ namespace SPNATI_Character_Editor
 							currentPoseDirective.Keyframes.Add(keyframe);
 						}
 						break;
+					case "choice":
+						if (currentDirective != null)
+						{
+							Choice choice = new Choice();
+							currentChoice = choice;
+							ParseAttributes(choice, value);
+							currentDirective.Choices.Add(choice);
+						}
+						break;
 					case "pose":
 						currentScene = null;
 						currentDirective = null;
 						currentEnding = null;
+						currentChoice = null;
 						currentPose = new Pose();
 						currentPose.Id = value;
 						currentPoseDirective = null;
@@ -1362,6 +1438,19 @@ namespace SPNATI_Character_Editor
 					case "direction":
 						line.Direction = value;
 						break;
+					case "oneShotId":
+						int oneShotId;
+						if (int.TryParse(value, out oneShotId))
+						{
+							lineCase.OneShotId = oneShotId;
+						}
+						break;
+					case "one-shot-id":
+						if (int.TryParse(value, out oneShotId))
+						{
+							line.OneShotId = oneShotId;
+						}
+						break;
 					case "set-gender":
 						line.Gender = value;
 						break;
diff --git a/editor source/SPNATI Character Editor/IO/Serialization.cs b/editor source/SPNATI Character Editor/IO/Serialization.cs
index 4d427544d3e69814fba3a2b6fbf7b7ce919e4466..405e301274159f724033eb44822500e3d1401281 100644
--- a/editor source/SPNATI Character Editor/IO/Serialization.cs	
+++ b/editor source/SPNATI Character Editor/IO/Serialization.cs	
@@ -57,6 +57,7 @@ namespace SPNATI_Character_Editor
 		/// <returns></returns>
 		public static bool ExportCharacter(Character character)
 		{
+			character.PrepareForEdit();
 			string dir = Config.GetRootDirectory(character);
 			if (!Directory.Exists(dir))
 			{
@@ -69,6 +70,13 @@ namespace SPNATI_Character_Editor
 				BackupAndExportXml(character, CharacterDatabase.GetEditorData(character), "editor", timestamp) &&
 				BackupAndExportXml(character, character.Collectibles, "collectibles", timestamp);
 
+			if (success && !string.IsNullOrEmpty(character.StyleSheetName))
+			{
+				CharacterStyleSheetSerializer.Save(character, character.Styles);
+			}
+
+			character.LastUpdate = DateTime.Now;
+
 			// clean up old files
 			DeleteFile(character, "markers.xml");
 			DeleteFile(character, "behaviour.edit.bak");
@@ -182,6 +190,8 @@ namespace SPNATI_Character_Editor
 				return null;
 			}
 
+			DateTime timestamp = File.GetLastWriteTime(filename);
+
 			if (string.IsNullOrEmpty(character.Version))
 			{
 				string contents = File.ReadAllText(filename);
@@ -223,6 +233,7 @@ namespace SPNATI_Character_Editor
 			{
 				character.Source = EditorSource.CharacterEditor;
 			}
+			character.LastUpdate = timestamp;
 
 			character.FolderName = Path.GetFileName(folderName);
 
diff --git a/editor source/SPNATI Character Editor/IO/SpnatiXmlSerializer.cs b/editor source/SPNATI Character Editor/IO/SpnatiXmlSerializer.cs
index cc54d5c3d2fe08aa8709288783d9aebacfda8601..bb8820d1f75fc11b23dee5b5ae8ddbc47b8cdc1c 100644
--- a/editor source/SPNATI Character Editor/IO/SpnatiXmlSerializer.cs	
+++ b/editor source/SPNATI Character Editor/IO/SpnatiXmlSerializer.cs	
@@ -1,8 +1,11 @@
-using System;
+using Desktop.CommonControls;
+using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Globalization;
 using System.IO;
+using System.Linq;
 using System.Reflection;
 using System.Text;
 using System.Xml;
@@ -143,10 +146,11 @@ namespace SPNATI_Character_Editor.IO
 				XmlAttributeAttribute attribute = field.Attribute;
 				if (attribute != null)
 				{
-					string value = field.GetValue(data)?.ToString();
 					foundSomething = true;
-					if (value == null)
+					object objValue = field.GetValue(data);
+					if (objValue == null)
 						continue;
+					string value = Convert.ToString(objValue, CultureInfo.InvariantCulture);
 					if (field.FieldType == typeof(bool))
 					{
 						value = value.ToLowerInvariant();
@@ -285,21 +289,29 @@ namespace SPNATI_Character_Editor.IO
 		{
 			Header = type.GetCustomAttribute<XmlHeaderAttribute>();
 
-			FieldInfo[] fields = type.GetFields();
-			foreach (FieldInfo field in fields)
+			MemberInfo[] fields = type.GetMembers();
+			foreach (MemberInfo field in fields)
 			{
-				if (field.GetCustomAttribute<XmlIgnoreAttribute>() != null)
-					continue;
+				if (field.MemberType == MemberTypes.Field || field.MemberType == MemberTypes.Property)
+				{
+					if (field.GetCustomAttribute<XmlIgnoreAttribute>() != null)
+						continue;
+
+					FieldInformation info = new FieldInformation(type, field);
+					Fields.Add(info);
+				}
+			}
 
-				FieldInformation info = new FieldInformation(type, field);
-				Fields.Add(info);
+			if (Fields.Any(fi => fi.Order != 0))
+			{
+				Fields.Sort();
 			}
 		}
 	}
 
-	public class FieldInformation
+	public class FieldInformation : IComparable<FieldInformation>
 	{
-		public FieldInfo Info;
+		public MemberInfo Info;
 		public Type FieldType;
 		public DefaultValueAttribute DefaultValue;
 		public XmlElementAttribute Element;
@@ -310,11 +322,12 @@ namespace SPNATI_Character_Editor.IO
 		public XmlNewLineAttribute NewLine;
 		public MethodInfo SortMethod;
 		public XmlAnyElementAttribute AnyElement;
+		public int Order;
 
-		public FieldInformation(Type parentType, FieldInfo field)
+		public FieldInformation(Type parentType, MemberInfo field)
 		{
 			Info = field;
-			FieldType = Info.FieldType;
+			FieldType = Info.GetDataType();
 			DefaultValue = field.GetCustomAttribute<DefaultValueAttribute>();
 			Element = field.GetCustomAttribute<XmlElementAttribute>();
 			Array = field.GetCustomAttribute<XmlArrayAttribute>();
@@ -323,6 +336,12 @@ namespace SPNATI_Character_Editor.IO
 			Attribute = field.GetCustomAttribute<XmlAttributeAttribute>();
 			NewLine = field.GetCustomAttribute<XmlNewLineAttribute>();
 
+			XmlOrderAttribute orderAttribute = field.GetCustomAttribute<XmlOrderAttribute>();
+			if (orderAttribute != null)
+			{
+				Order = orderAttribute.SortOrder;
+			}
+
 			XmlSortMethodAttribute sortAttribute = field.GetCustomAttribute<XmlSortMethodAttribute>();
 			if (sortAttribute != null)
 			{
@@ -330,13 +349,22 @@ namespace SPNATI_Character_Editor.IO
 			}
 
 			AnyElement = field.GetCustomAttribute<XmlAnyElementAttribute>();
-
 		}
 
 		public object GetValue(object obj)
 		{
 			return Info.GetValue(obj);
 		}
+
+		public override string ToString()
+		{
+			return Info.Name;
+		}
+
+		public int CompareTo(FieldInformation other)
+		{
+			return Order.CompareTo(other.Order);
+		}
 	}
 
 	[AttributeUsage(AttributeTargets.Field)]
@@ -382,4 +410,14 @@ namespace SPNATI_Character_Editor.IO
 			Method = method;
 		}
 	}
+
+	public class XmlOrderAttribute : Attribute
+	{
+		public int SortOrder { get; set; }
+
+		public XmlOrderAttribute(int order)
+		{
+			SortOrder = order;
+		}
+	}
 }
diff --git a/editor source/SPNATI Character Editor/Icons/AddPause.png b/editor source/SPNATI Character Editor/Icons/AddPause.png
new file mode 100644
index 0000000000000000000000000000000000000000..d35a00186b369df4515f64fdbe8e404c6445849e
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Icons/AddPause.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2baf56a43bd11fa788668d35744a469b811e69812f5389a82f397abcd2468bd3
+size 506
diff --git a/editor source/SPNATI Character Editor/Icons/GIF.png b/editor source/SPNATI Character Editor/Icons/GIF.png
new file mode 100644
index 0000000000000000000000000000000000000000..2bc735f9ff3ed35e62969e5232d68007fda5a3a2
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Icons/GIF.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f1ef91625789fc67a2b8f97d81854de5c5e05411535771c807fc737e98dcd2f1
+size 272
diff --git a/editor source/SPNATI Character Editor/Icons/GoToLine.png b/editor source/SPNATI Character Editor/Icons/GoToLine.png
index b486183d594d759f9e70455e31048a8ecb7637c3..0f22b9355bd26e9c7859aeb2d080b84883e59a8a 100644
--- a/editor source/SPNATI Character Editor/Icons/GoToLine.png	
+++ b/editor source/SPNATI Character Editor/Icons/GoToLine.png	
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:8691b603de76e46dedc48aca252ceaf92db864019247b70d7c09288e68fea778
-size 21313
+oid sha256:f3efe0381784072cdf138cfe165a4152409a42bab1e6fd314f7ce63a5cb20e86
+size 313
diff --git a/editor source/SPNATI Character Editor/Icons/Image.png b/editor source/SPNATI Character Editor/Icons/Image.png
new file mode 100644
index 0000000000000000000000000000000000000000..519e719ce2989f64e86fb96be89bb03dd5ed92e1
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Icons/Image.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0fc115d3718969244e34b070d3291ef54e7229f5595a06f8da6074ac98cc0cd4
+size 298
diff --git a/editor source/SPNATI Character Editor/Icons/Legend.png b/editor source/SPNATI Character Editor/Icons/Legend.png
new file mode 100644
index 0000000000000000000000000000000000000000..c275cbeb0437f564def93531a1a21e99b92eacd0
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Icons/Legend.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:806cf01b38a63d94d1c0ec32b7aba169ee3edd4df73766186e2a43fc59d40720
+size 225
diff --git a/editor source/SPNATI Character Editor/Icons/Recipe.png b/editor source/SPNATI Character Editor/Icons/Recipe.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a2f78149b9eb0211c99084a76b27bbd91cdd043
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Icons/Recipe.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:82e00ace7acfc5d5339a97f35cbbf55d656c24d6b7b9904bb4f6d4585ef30379
+size 382
diff --git a/editor source/SPNATI Character Editor/Icons/Refresh.png b/editor source/SPNATI Character Editor/Icons/Refresh.png
new file mode 100644
index 0000000000000000000000000000000000000000..3dda390f72621115e48fd7dfa41a3cca756152d6
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Icons/Refresh.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e0fd05663c6ac806a8ad1c65a9a5684ef4162684fab4805ad63775cb69861727
+size 591
diff --git a/editor source/SPNATI Character Editor/Icons/Remove.png b/editor source/SPNATI Character Editor/Icons/Remove.png
index 3413791166904d68dbb921dac1d617aa23b7b704..ff8f6a90e2bba8b8aa715062f38b647b559d6721 100644
--- a/editor source/SPNATI Character Editor/Icons/Remove.png	
+++ b/editor source/SPNATI Character Editor/Icons/Remove.png	
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:b99cd58dc9820681cd32f6e3fcd5207d667fc164768f54558382a477cf3be016
-size 21145
+oid sha256:1b8fa3ea1fe1617146227ce9ad4a1675321d48747ca2a0b6364ae82745779ca4
+size 229
diff --git a/editor source/SPNATI Character Editor/Icons/Settings.png b/editor source/SPNATI Character Editor/Icons/Settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce7d24332d4066b6eac9a5d64e3fb1a535c1905b
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Icons/Settings.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d7ef8e80458ebe5b0536ae7175710a9a27b21a931a7331e4bede8e3742a1675f
+size 420
diff --git a/editor source/SPNATI Character Editor/Icons/Sort.png b/editor source/SPNATI Character Editor/Icons/Sort.png
new file mode 100644
index 0000000000000000000000000000000000000000..5e34a59c781ad76b67c8131dc159c744a34faea3
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Icons/Sort.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2cbf47eb10a2539e3a329406a5160580473e5667473e05e1db5b099b9af3132d
+size 356
diff --git a/editor source/SPNATI Character Editor/Icons/SpeechBubble.png b/editor source/SPNATI Character Editor/Icons/SpeechBubble.png
new file mode 100644
index 0000000000000000000000000000000000000000..7d7172a142f5f16ccde044e99e2e8f6839aa4e03
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Icons/SpeechBubble.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6a71fa3525f3bf0ff7c71335328b659a3a62a471b9679b392fe759d40f9a72f1
+size 296
diff --git a/editor source/SPNATI Character Editor/Icons/SplitKeyframe.png b/editor source/SPNATI Character Editor/Icons/SplitKeyframe.png
index f3e798853d68b8f2ccd0298a760da49abefae454..9526aa0eb627d711801123991b391b2f383bb7ed 100644
--- a/editor source/SPNATI Character Editor/Icons/SplitKeyframe.png	
+++ b/editor source/SPNATI Character Editor/Icons/SplitKeyframe.png	
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:c861a16b38acd62c133ff013b2414b1b09526951c3a7bb123a9ae079f020a495
-size 262
+oid sha256:35d91d00bf14de96b4f1674f30b8904115efce5f632f3cf40617d7677d845513
+size 369
diff --git a/editor source/SPNATI Character Editor/Icons/Theme.png b/editor source/SPNATI Character Editor/Icons/Theme.png
new file mode 100644
index 0000000000000000000000000000000000000000..d3542e3b47c99216003118b02b6bb5259b48654a
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Icons/Theme.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:27d8248355c579b25610f73886fff9459c9c99b33a67a320c136bf3c04f7f035
+size 212
diff --git a/editor source/SPNATI Character Editor/ImageLibrary.cs b/editor source/SPNATI Character Editor/ImageLibrary.cs
index 12aed65d8c205d3c9f64b907fac094038d74f891..2d6db135e7d0586b938878de04602959c0cbe366 100644
--- a/editor source/SPNATI Character Editor/ImageLibrary.cs	
+++ b/editor source/SPNATI Character Editor/ImageLibrary.cs	
@@ -273,6 +273,28 @@ namespace SPNATI_Character_Editor
 				}
 			}
 		}
+
+		public bool FilterImage(Character character, string file)
+		{
+			string prefix = Config.PrefixFilter;
+			if (!string.IsNullOrEmpty(prefix) && file.StartsWith(prefix))
+			{
+				return true;
+			}
+
+			CharacterEditorData editorData = CharacterDatabase.GetEditorData(character);
+			if (editorData != null)
+			{
+				foreach (string p in editorData.IgnoredPrefixes)
+				{
+					if (file.StartsWith(p))
+					{
+						return true;
+					}
+				}
+			}
+			return false;
+		}
 	}
 
 	public class ImageReplacementArgs
diff --git a/editor source/SPNATI Character Editor/Intellisense.cs b/editor source/SPNATI Character Editor/Intellisense.cs
index 4705e44aa634c04ee39ff974d310abe1afd045be..b22ebda9ad27d322de31ab7e8fd1bfb88788e13b 100644
--- a/editor source/SPNATI Character Editor/Intellisense.cs	
+++ b/editor source/SPNATI Character Editor/Intellisense.cs	
@@ -24,6 +24,12 @@ namespace SPNATI_Character_Editor
 							contexts.Push(currentContext);
 							sb = currentContext.Builder;
 						}
+						else if (c == '{')
+						{
+							currentContext = new IntellisenseContext(ContextType.StyleName, i);
+							contexts.Push(currentContext);
+							sb = currentContext.Builder;
+						}
 						break;
 					case ContextType.VariableName:
 						if (c == '.')
@@ -101,6 +107,21 @@ namespace SPNATI_Character_Editor
 							sb.Append(c);
 						}
 						break;
+					case ContextType.StyleName:
+						if (c == '}' || (!char.IsLetterOrDigit(c) && c != '_' && c != '!'))
+						{
+							//End of style, so go up. We don't need to remember anything from this context
+							contexts.Pop();
+							currentContext = contexts.Peek();
+							currentContext.Builder.Append("{" + sb.ToString() + "}");
+							sb = currentContext.Builder;
+							break;
+						}
+						else
+						{
+							sb.Append(c);
+						}
+						break;
 					case ContextType.FunctionEnd:
 						//regardless of the character, we're at the end of the function or have invalid syntax for one, so we can just reset
 						contexts.Pop();
@@ -167,6 +188,9 @@ namespace SPNATI_Character_Editor
 				case ContextType.VariableName:
 					VariableName = Builder.ToString();
 					break;
+				case ContextType.StyleName:
+					VariableName = Builder.ToString();
+					break;
 				case ContextType.FunctionName:
 					FunctionName = Builder.ToString();
 					break;
@@ -202,5 +226,9 @@ namespace SPNATI_Character_Editor
 		/// Next character should be a ~ or it was all invalid
 		/// </summary>
 		FunctionEnd,
+		/// <summary>
+		/// Reading a style name
+		/// </summary>
+		StyleName,
 	}
 }
diff --git a/editor source/SPNATI Character Editor/NewCharacterPrompt.Designer.cs b/editor source/SPNATI Character Editor/NewCharacterPrompt.Designer.cs
index 06ba0bc783165d2d73cfb057143924a926704389..1e89899b2784237af2c2153b621a9c2a49a9daeb 100644
--- a/editor source/SPNATI Character Editor/NewCharacterPrompt.Designer.cs	
+++ b/editor source/SPNATI Character Editor/NewCharacterPrompt.Designer.cs	
@@ -28,16 +28,19 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.cmdOK = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.txtName = new System.Windows.Forms.TextBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdOK = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.txtName = new Desktop.Skinning.SkinnedTextBox();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(13, 13);
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.label1.Location = new System.Drawing.Point(13, 40);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(38, 13);
 			this.label1.TabIndex = 1;
@@ -46,19 +49,27 @@
 			// cmdOK
 			// 
 			this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOK.Location = new System.Drawing.Point(199, 42);
+			this.cmdOK.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOK.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOK.Flat = false;
+			this.cmdOK.ForeColor = System.Drawing.Color.White;
+			this.cmdOK.Location = new System.Drawing.Point(208, 3);
 			this.cmdOK.Name = "cmdOK";
 			this.cmdOK.Size = new System.Drawing.Size(75, 23);
 			this.cmdOK.TabIndex = 2;
-			this.cmdOK.Text = "OK";
+			this.cmdOK.Text = "Create";
 			this.cmdOK.UseVisualStyleBackColor = true;
 			this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click);
 			// 
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(280, 42);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(289, 3);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 3;
@@ -70,35 +81,51 @@
 			// 
 			this.txtName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.txtName.Location = new System.Drawing.Point(57, 10);
+			this.txtName.BackColor = System.Drawing.Color.White;
+			this.txtName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtName.ForeColor = System.Drawing.Color.Black;
+			this.txtName.Location = new System.Drawing.Point(57, 37);
 			this.txtName.Name = "txtName";
 			this.txtName.Size = new System.Drawing.Size(298, 20);
 			this.txtName.TabIndex = 0;
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOK);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 74);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(367, 30);
+			this.skinnedPanel1.TabIndex = 4;
+			// 
 			// NewCharacterPrompt
 			// 
 			this.AcceptButton = this.cmdOK;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(367, 77);
+			this.ClientSize = new System.Drawing.Size(367, 104);
 			this.ControlBox = false;
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.txtName);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOK);
 			this.Controls.Add(this.label1);
 			this.Name = "NewCharacterPrompt";
+			this.ShowIcon = false;
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Create New Character";
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
 		}
 
 		#endregion
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Button cmdOK;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.TextBox txtName;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedButton cmdOK;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedTextBox txtName;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/NewCharacterPrompt.cs b/editor source/SPNATI Character Editor/NewCharacterPrompt.cs
index 2427aa75c187f58eb4bfab1a61e53d5e265ec8f5..dc4be5ab07fa9b7b3d7f5ef5c53170eae7f9e2b9 100644
--- a/editor source/SPNATI Character Editor/NewCharacterPrompt.cs	
+++ b/editor source/SPNATI Character Editor/NewCharacterPrompt.cs	
@@ -1,11 +1,11 @@
-using SPNATI_Character_Editor.Providers;
+using Desktop.Skinning;
+using SPNATI_Character_Editor.Providers;
 using System;
-using System.Collections.Generic;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor
 {
-	public partial class NewCharacterPrompt : Form
+	public partial class NewCharacterPrompt : SkinnedForm
 	{
 		public Character Character { get; private set; }
 
diff --git a/editor source/SPNATI Character Editor/Newtonsoft.Json.dll b/editor source/SPNATI Character Editor/Newtonsoft.Json.dll
new file mode 100644
index 0000000000000000000000000000000000000000..be6558d2d48b3c254ec4adb2aae004597b832cac
Binary files /dev/null and b/editor source/SPNATI Character Editor/Newtonsoft.Json.dll differ
diff --git a/editor source/SPNATI Character Editor/Program.cs b/editor source/SPNATI Character Editor/Program.cs
index bc8a32be041697dca03f55f1ae8b107b4311a978..187fa427eaa371450f98f6821ce451aaf521d007 100644
--- a/editor source/SPNATI Character Editor/Program.cs	
+++ b/editor source/SPNATI Character Editor/Program.cs	
@@ -1,11 +1,20 @@
 using Desktop;
+using Desktop.Skinning;
+using SPNATI_Character_Editor.Forms;
 using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.IO;
+using System.IO.Compression;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor
 {
 	static class Program
 	{
+		private static WorkflowTracker _workflowFilter;
+
 		/// <summary>
 		/// The main entry point for the application.
 		/// </summary>
@@ -14,8 +23,27 @@ namespace SPNATI_Character_Editor
 		{
 			Application.EnableVisualStyles();
 			Application.SetCompatibleTextRenderingDefault(false);
-			AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
+
+			if (!Debugger.IsAttached)
+			{
+				_workflowFilter = new WorkflowTracker();
+				Application.AddMessageFilter(_workflowFilter);
+				AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
+			}
+
 			Shell shell = new Shell("SPNATI Character Editor", Properties.Resources.editor);
+			SkinManager.Instance.LoadSkins(Path.Combine("Resources", "Skins"));
+			string skinName = Config.Skin;
+			Skin skin = SkinManager.Instance.AvailableSkins.Find(s => s.Name == skinName);
+			if (skin == null)
+			{
+				skin = SkinManager.Instance.AvailableSkins.Find(s => s.Name == "Blue");
+			}
+			if (skin != null)
+			{
+				SkinManager.Instance.SetSkin(skin);
+			}
+
 			shell.Load += Shell_Load;
 			shell.FormClosed += Shell_FormClosed;
 			Application.Run(shell);
@@ -33,7 +61,46 @@ namespace SPNATI_Character_Editor
 
 		static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
 		{
-			ErrorLog.LogError((e.ExceptionObject as Exception).StackTrace);
+			try
+			{
+				Exception exception = e.ExceptionObject as Exception;
+				string stack = exception.StackTrace;
+				List<string> errorData = new List<string>();
+				errorData.Add(exception.Message);
+				errorData.Add(stack);
+
+				string date = DateTime.Now.ToString();
+				date = date.Replace('/', '-');
+				date = date.Replace('\\', '-');
+				date = date.Replace(":", "");
+				date = date.Replace("AM", "");
+				date = date.Replace("PM", "");
+				date = date.Replace(" ", "");
+				string dir = Path.Combine(Config.AppDataDirectory, date);
+				Directory.CreateDirectory(dir);
+
+				string crashLog = Path.Combine(dir, "crash.txt");
+				File.WriteAllLines(crashLog, errorData);
+
+				int count = 1;
+				foreach (Bitmap bmp in _workflowFilter.GetScreens())
+				{
+					string file = Path.Combine(dir, "capture" + count + ".png");
+					bmp.Save(file);
+					count++;
+				}
+
+				ErrorLog.LogError(stack);
+
+				string zip = Path.Combine(Config.AppDataDirectory, "crashdetails.zip");
+				ZipFile.CreateFromDirectory(dir, Path.Combine(dir, zip));
+				File.Move(zip, Path.Combine(dir, "crashdetails.zip"));
+
+				ErrorTrace trace = new ErrorTrace();
+				trace.SetPath(dir);
+				trace.ShowDialog();
+			}
+			catch {	}
 		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/ProgressForm.Designer.cs b/editor source/SPNATI Character Editor/ProgressForm.Designer.cs
index 4cf63da0d4c03be4068ce909c49ed5b93993a1ec..f4162bc2bb52f56ec7f2273355ab9fe0a00dea22 100644
--- a/editor source/SPNATI Character Editor/ProgressForm.Designer.cs	
+++ b/editor source/SPNATI Character Editor/ProgressForm.Designer.cs	
@@ -28,14 +28,16 @@
 		/// </summary>
 		private void InitializeComponent()
 		{
-			this.progressBar = new System.Windows.Forms.ProgressBar();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.lblProgress = new System.Windows.Forms.Label();
+			this.progressBar = new Desktop.Skinning.SkinnedProgressBar();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.lblProgress = new Desktop.Skinning.SkinnedLabel();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.skinnedPanel1.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// progressBar
 			// 
-			this.progressBar.Location = new System.Drawing.Point(13, 13);
+			this.progressBar.Location = new System.Drawing.Point(13, 38);
 			this.progressBar.Name = "progressBar";
 			this.progressBar.Size = new System.Drawing.Size(259, 23);
 			this.progressBar.TabIndex = 0;
@@ -43,8 +45,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(197, 72);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdCancel.Flat = false;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(206, 4);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 1;
@@ -54,34 +60,49 @@
 			// 
 			// lblProgress
 			// 
-			this.lblProgress.Location = new System.Drawing.Point(13, 43);
+			this.lblProgress.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
+			this.lblProgress.Location = new System.Drawing.Point(13, 68);
 			this.lblProgress.Name = "lblProgress";
 			this.lblProgress.Size = new System.Drawing.Size(259, 23);
 			this.lblProgress.TabIndex = 2;
 			this.lblProgress.Text = "Validating 0 o 20...";
 			this.lblProgress.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
 			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 104);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(284, 30);
+			this.skinnedPanel1.TabIndex = 3;
+			// 
 			// ProgressForm
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(284, 107);
+			this.ClientSize = new System.Drawing.Size(284, 134);
+			this.Controls.Add(this.skinnedPanel1);
 			this.Controls.Add(this.lblProgress);
-			this.Controls.Add(this.cmdCancel);
 			this.Controls.Add(this.progressBar);
+			this.MaximizeBox = false;
+			this.MinimizeBox = false;
 			this.Name = "ProgressForm";
 			this.ShowIcon = false;
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
 			this.Text = "Progress";
+			this.skinnedPanel1.ResumeLayout(false);
 			this.ResumeLayout(false);
 
 		}
 
 		#endregion
 
-		private System.Windows.Forms.ProgressBar progressBar;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.Label lblProgress;
+		private Desktop.Skinning.SkinnedProgressBar progressBar;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedLabel lblProgress;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/ProgressForm.cs b/editor source/SPNATI Character Editor/ProgressForm.cs
index 8c70086ede2e881bbb4ed3e7a60305ae34a3d91c..1627c5fae42b6792b9b12a424608c988f5a75edb 100644
--- a/editor source/SPNATI Character Editor/ProgressForm.cs	
+++ b/editor source/SPNATI Character Editor/ProgressForm.cs	
@@ -1,10 +1,10 @@
-using System;
+using Desktop.Skinning;
+using System;
 using System.Threading;
-using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor
 {
-	public partial class ProgressForm : Form
+	public partial class ProgressForm : SkinnedForm
 	{
 		private CancellationTokenSource _cts;
 
diff --git a/editor source/SPNATI Character Editor/Properties/AssemblyInfo.cs b/editor source/SPNATI Character Editor/Properties/AssemblyInfo.cs
index 320321db99c1dad9e2e6ae5fa5c08a54512e2ef1..7bf252760cee66bddc30e6ce94e006bdc50a8698 100644
--- a/editor source/SPNATI Character Editor/Properties/AssemblyInfo.cs	
+++ b/editor source/SPNATI Character Editor/Properties/AssemblyInfo.cs	
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("4.3.0.0")]
+[assembly: AssemblyVersion("5.0.0.0")]
 [assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/editor source/SPNATI Character Editor/Properties/Resources.Designer.cs b/editor source/SPNATI Character Editor/Properties/Resources.Designer.cs
index 7c81a585feeaab81a34f9d526ba78d8f73009aff..48e8ddb707c0282788d5cea5fd97186062d1985c 100644
--- a/editor source/SPNATI Character Editor/Properties/Resources.Designer.cs	
+++ b/editor source/SPNATI Character Editor/Properties/Resources.Designer.cs	
@@ -110,6 +110,16 @@ namespace SPNATI_Character_Editor.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap AddPause {
+            get {
+                object obj = ResourceManager.GetObject("AddPause", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
@@ -590,6 +600,16 @@ namespace SPNATI_Character_Editor.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap GIF {
+            get {
+                object obj = ResourceManager.GetObject("GIF", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
@@ -630,6 +650,16 @@ namespace SPNATI_Character_Editor.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Image {
+            get {
+                object obj = ResourceManager.GetObject("Image", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
@@ -640,6 +670,16 @@ namespace SPNATI_Character_Editor.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Legend {
+            get {
+                object obj = ResourceManager.GetObject("Legend", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
@@ -750,6 +790,16 @@ namespace SPNATI_Character_Editor.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Recipe {
+            get {
+                object obj = ResourceManager.GetObject("Recipe", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
@@ -770,6 +820,16 @@ namespace SPNATI_Character_Editor.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Refresh {
+            get {
+                object obj = ResourceManager.GetObject("Refresh", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
@@ -830,6 +890,36 @@ namespace SPNATI_Character_Editor.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Settings {
+            get {
+                object obj = ResourceManager.GetObject("Settings", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Sort {
+            get {
+                object obj = ResourceManager.GetObject("Sort", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap SpeechBubble {
+            get {
+                object obj = ResourceManager.GetObject("SpeechBubble", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
@@ -850,6 +940,16 @@ namespace SPNATI_Character_Editor.Properties {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Theme {
+            get {
+                object obj = ResourceManager.GetObject("Theme", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
diff --git a/editor source/SPNATI Character Editor/Properties/Resources.resx b/editor source/SPNATI Character Editor/Properties/Resources.resx
index fa67f6d724663972131dee6563dd53d5997907fd..4771fb5f04c2aea1c5fd1b1982f5e9b7111987bd 100644
--- a/editor source/SPNATI Character Editor/Properties/Resources.resx	
+++ b/editor source/SPNATI Character Editor/Properties/Resources.resx	
@@ -397,4 +397,34 @@
   <data name="Ellipsis" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Icons\Ellipsis.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
+  <data name="Refresh" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\Refresh.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Image" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\Image.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="GIF" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\GIF.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Legend" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\Legend.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Recipe" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\Recipe.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Settings" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\Settings.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Theme" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\Theme.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Sort" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\Sort.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="AddPause" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\AddPause.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="SpeechBubble" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Icons\SpeechBubble.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
 </root>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Providers/CharacterProvider.cs b/editor source/SPNATI Character Editor/Providers/CharacterProvider.cs
index 0d6d3098123f6f0e7632f6d5f0bc2bacb7dbeeaa..3415acece2e41703c1b1a0f89441f74f3936799e 100644
--- a/editor source/SPNATI Character Editor/Providers/CharacterProvider.cs	
+++ b/editor source/SPNATI Character Editor/Providers/CharacterProvider.cs	
@@ -21,7 +21,7 @@ namespace SPNATI_Character_Editor.Providers
 
 		public bool AllowsNew
 		{
-			get	{ return true; }
+			get { return true; }
 		}
 
 		public bool TrackRecent
@@ -90,17 +90,20 @@ namespace SPNATI_Character_Editor.Providers
 
 		public void Sort(List<IRecord> list)
 		{
-			list.Sort((record1, record2) => record1.CompareTo(record2));	
+			list.Sort((record1, record2) => record1.CompareTo(record2));
 		}
 
 		public string[] GetColumns()
 		{
-			return new string[] { "Name", "Folder" };
+			return new string[] { "Name", "Folder", "Last Update", "Source" };
 		}
 
 		public ListViewItem FormatItem(IRecord record)
 		{
-			return new ListViewItem(new string[] { record.Name, record.Key });
+			OpponentStatus status = Listing.Instance.GetCharacterStatus(record.Key);
+			Character c = record as Character;
+			string lastUpdate = GetTimeSince(c.LastUpdate, DateTime.Now);
+			return new ListViewItem(new string[] { record.Name, record.Key, lastUpdate, status == OpponentStatus.Testing || status == OpponentStatus.Main ? "" : status.ToString() });
 		}
 
 		public void SetContext(object context)
@@ -108,5 +111,51 @@ namespace SPNATI_Character_Editor.Providers
 			_characterContext = context as Character;
 			_skinContext = context as Costume;
 		}
+
+		public static string GetTimeSince(DateTime date, DateTime since)
+		{
+			TimeSpan diff = date - since;
+			if (date <= since)
+			{
+				if (diff.Days <= -7)
+				{
+					int days = -diff.Days;
+					int weeks = days / 7;
+					int months = weeks / 4;
+					if (months >= 12)
+					{
+						return "Over a year ago";
+					}
+					else if (months == 0)
+					{
+						return $"{weeks} {(weeks == 1 ? "week" : "weeks")} ago";
+					}
+					return $"{months} {(months == 1 ? "month" : "months")} ago";
+				}
+				else
+				{
+					if (diff.Days < 0)
+					{
+						return $"{-diff.Days} {(diff.Days == -1 ? "day" : "days")} ago";
+					}
+					else
+					{
+						if (diff.Hours < 0)
+						{
+							return $"{-diff.Hours} {(diff.Hours == -1 ? "hour" : "hours")} ago";
+						}
+						else
+						{
+							if (diff.Minutes >= 0)
+							{
+								return "Just now";
+							}
+							return $"{-diff.Minutes} {(diff.Minutes == -1 ? "minute" : "minutes")} ago";
+						}
+					}
+				}
+			}
+			return "In the future";
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/Providers/SkinProvider.cs b/editor source/SPNATI Character Editor/Providers/CostumeProvider.cs
similarity index 97%
rename from editor source/SPNATI Character Editor/Providers/SkinProvider.cs
rename to editor source/SPNATI Character Editor/Providers/CostumeProvider.cs
index e6dbd674a5dde365bed72ea885698fa225f8facb..76944e90e70128b4233887a41a5dac15d659ce45 100644
--- a/editor source/SPNATI Character Editor/Providers/SkinProvider.cs	
+++ b/editor source/SPNATI Character Editor/Providers/CostumeProvider.cs	
@@ -5,7 +5,7 @@ using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor.Providers
 {
-	public class SkinProvider : IRecordProvider<Costume>
+	public class CostumeProvider : IRecordProvider<Costume>
 	{
 		public string GetLookupCaption()
 		{
@@ -75,6 +75,7 @@ namespace SPNATI_Character_Editor.Providers
 					list.Add(record);
 				}
 			}
+
 			return list;
 		}
 
diff --git a/editor source/SPNATI Character Editor/Providers/DefinitionProvider.cs b/editor source/SPNATI Character Editor/Providers/DefinitionProvider.cs
index 5d0f024355063639f50f252ea228d9a5c911bbce..f4c3e3edb0b876611ddf977a5c70a8f266ceaadf 100644
--- a/editor source/SPNATI Character Editor/Providers/DefinitionProvider.cs	
+++ b/editor source/SPNATI Character Editor/Providers/DefinitionProvider.cs	
@@ -15,12 +15,17 @@ namespace SPNATI_Character_Editor.Providers
 		}
 
 		public IRecord Create(string key)
+		{
+			T definition = CreateRecord(key);
+			ApplyDefaults(definition);
+			Definitions.Instance.Add(definition);
+			return definition;
+		}
+		protected virtual T CreateRecord(string key)
 		{
 			T definition = Activator.CreateInstance<T>();
 			definition.Key = key;
 			definition.Name = key;
-			ApplyDefaults(definition);
-			Definitions.Instance.Add(definition);
 			return definition;
 		}
 		public abstract void ApplyDefaults(T definition);
diff --git a/editor source/SPNATI Character Editor/Providers/StyleControlProvider.cs b/editor source/SPNATI Character Editor/Providers/StyleControlProvider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8abd31d5c5fe3111a7f1f172c142090bbfc86efe
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Providers/StyleControlProvider.cs	
@@ -0,0 +1,47 @@
+using SPNATI_Character_Editor.Controls.StyleControls;
+using System;
+using System.Reflection;
+
+namespace SPNATI_Character_Editor.Providers
+{
+	public class StyleControlProvider : DefinitionProvider<StyleAttributeRecord>
+	{
+		static StyleControlProvider()
+		{
+			Definitions.Instance.Add(new StyleAttributeRecord(null, "other", "Free text attribute"));
+			Assembly assembly = typeof(StyleControlProvider).Assembly;
+			foreach (Type type in assembly.GetTypes())
+			{
+				foreach (SubAttributeAttribute attrib in type.GetCustomAttributes<SubAttributeAttribute>())
+				{
+					StyleAttributeRecord definition = new StyleAttributeRecord(type, attrib.Attribute, attrib.Description);
+					Definitions.Instance.Add(definition);
+				}
+			}
+		}
+
+		public override void ApplyDefaults(StyleAttributeRecord definition)
+		{
+			
+		}
+
+		public override string GetLookupCaption()
+		{
+			return "Choose an Attribute to Add";
+		}
+	}
+
+	public class StyleAttributeRecord : Definition
+	{
+		public Type ControlType;
+
+		public StyleAttributeRecord() { }
+
+		public StyleAttributeRecord(Type type, string attribute, string description)
+		{
+			ControlType = type;
+			Key = Name = attribute;
+			Description = description;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Providers/TargetProvider.cs b/editor source/SPNATI Character Editor/Providers/TargetProvider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..04e0d5456f74c8d46275ecc023c2a31ada896ad4
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Providers/TargetProvider.cs	
@@ -0,0 +1,99 @@
+using Desktop;
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor.Providers
+{
+	public class TargetProvider : IRecordProvider<TargetId>
+	{
+		private Case _context;
+
+		public bool AllowsNew
+		{
+			get
+			{
+				return true;
+			}
+		}
+
+		public bool TrackRecent
+		{
+			get
+			{
+				return false;
+			}
+		}
+
+		public IRecord Create(string key)
+		{
+			return new TargetId(key, key, "Characters", "");
+		}
+
+		public void Delete(IRecord record)
+		{
+			throw new NotImplementedException();
+		}
+
+		public ListViewItem FormatItem(IRecord record)
+		{
+			TargetId target = record as TargetId;
+			return new ListViewItem(new string[] { target.Name, target.Key, target.Description });
+		}
+
+		public string[] GetColumns()
+		{
+			return new string[] { "Name", "Folder" };
+		}
+
+		public string GetLookupCaption()
+		{
+			return "Target Select";
+		}
+
+		public List<IRecord> GetRecords(string text)
+		{
+			text = text.ToLower();
+			List<IRecord> list = new List<IRecord>();
+			if ("self".StartsWith(text))
+			{
+				list.Add(new TargetId("self", "Self", "Targeted", "Looks at this character's information"));
+			}
+			if ("target".StartsWith(text))
+			{
+				list.Add(new TargetId("target", "Target", "Targeted", "Looks at the current case's target's information if there is one"));
+			}
+
+			if (_context != null)
+			{
+				foreach (TargetCondition filter in _context.Conditions)
+				{
+					if (!string.IsNullOrEmpty(filter.Variable))
+					{
+						list.Add(new TargetId(filter.Variable, filter.Variable, "Filters", "Custom filter variable"));
+					}
+				}
+			}
+
+			foreach (Character record in CharacterDatabase.Characters)
+			{
+				if (record.Key.ToLower().Contains(text) || record.Name.ToLower().Contains(text))
+				{
+					//partial match
+					list.Add(new TargetId(CharacterDatabase.GetId(record), record.Name, "Characters", "Looks at " + record.FirstName + "'s information if they are in the game"));
+				}
+			}
+			return list;
+		}
+
+		public void SetContext(object context)
+		{
+			_context = context as Case;
+		}
+
+		public void Sort(List<IRecord> list)
+		{
+			list.Sort((record1, record2) => record1.CompareTo(record2));
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/15f3a04f-8e03-40e0-b426-dcba8b4f5b3b.txt b/editor source/SPNATI Character Editor/Resources/Recipes/15f3a04f-8e03-40e0-b426-dcba8b4f5b3b.txt
new file mode 100644
index 0000000000000000000000000000000000000000..73e6fb5e0fe209dd49df3f731c0d581ac1f494c0
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/15f3a04f-8e03-40e0-b426-dcba8b4f5b3b.txt	
@@ -0,0 +1,28 @@
+{
+  "case": {
+    "tag": "hand",
+    "oneShotId": 0,
+    "totalAlive": "2",
+    "counters": [
+      {
+        "count": "0",
+        "gender": "",
+        "status": "not_lost_all",
+        "HasAdvancedConditions": true
+      },
+      {
+        "count": "1",
+        "gender": "",
+        "status": "alive",
+        "role": "opp",
+        "var": "opponent",
+        "HasAdvancedConditions": true
+      }
+    ],
+    "tests": []
+  },
+  "name": "Last Round",
+  "key": "15f3a04f-8e03-40e0-b426-dcba8b4f5b3b",
+  "group": "Poker",
+  "description": "This plays during the hand quality phase for the final round of the game, if your player is one of the two remaining players. The variable ~opponent~ will contain the name of the other player."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/1ac2b058-5e06-4321-9ea7-d6bc3098748e.txt b/editor source/SPNATI Character Editor/Resources/Recipes/1ac2b058-5e06-4321-9ea7-d6bc3098748e.txt
new file mode 100644
index 0000000000000000000000000000000000000000..07835f68bd9e9e86925ded15e08c057484c5e9f4
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/1ac2b058-5e06-4321-9ea7-d6bc3098748e.txt	
@@ -0,0 +1,20 @@
+{
+  "case": {
+    "tag": "opponent_stripped",
+    "oneShotId": 1,
+    "targetStatus": "exposed",
+    "totalExposed": "1",
+    "counters": [],
+    "tests": [
+      {
+        "expr": "~target.biggestlead~",
+        "cmp": ">=",
+        "value": "3"
+      }
+    ]
+  },
+  "name": "Choke",
+  "key": "1ac2b058-5e06-4321-9ea7-d6bc3098748e",
+  "group": "Stripping",
+  "description": "A character was leading by at least 3 articles of clothing at one point but is now the first one to reveal their chest or crotch."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/40a78c86-ea36-42da-b84b-039a1e3c2a9c.txt b/editor source/SPNATI Character Editor/Resources/Recipes/40a78c86-ea36-42da-b84b-039a1e3c2a9c.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6caf49c08c55b2f6edff1a33ac9a17837ceb02d4
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/40a78c86-ea36-42da-b84b-039a1e3c2a9c.txt	
@@ -0,0 +1,26 @@
+{
+  "case": {
+    "tag": "finished_masturbating",
+    "counters": [
+      {
+        "count": "0",
+        "gender": "",
+        "status": "not_lost_all",
+        "var": "",
+        "HasAdvancedConditions": true
+      },
+      {
+        "count": "2",
+        "gender": "",
+        "status": "alive",
+        "var": "playing",
+        "HasAdvancedConditions": true
+      }
+    ],
+    "tests": []
+  },
+  "name": "Last Round (Observer)",
+  "key": "40a78c86-ea36-42da-b84b-039a1e3c2a9c",
+  "group": "Poker",
+  "description": "This plays during the hand quality phase for the final round of the game when your player is already out. The variable ~playing~ will display the name of one of the remaining players."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/43e68bf4-4dc8-4938-a5d7-8f1e17e1202e.txt b/editor source/SPNATI Character Editor/Resources/Recipes/43e68bf4-4dc8-4938-a5d7-8f1e17e1202e.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a114260cd2c555f867b43de89c67a821f638f3c1
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/43e68bf4-4dc8-4938-a5d7-8f1e17e1202e.txt	
@@ -0,0 +1,28 @@
+{
+  "case": {
+    "tag": "hand",
+    "totalAlive": "2",
+    "counters": [
+      {
+        "count": "1",
+        "filter": "human",
+        "gender": "",
+        "status": "alive",
+        "var": "",
+        "HasAdvancedConditions": true
+      },
+      {
+        "count": "0",
+        "gender": "",
+        "status": "not_lost_all",
+        "var": "",
+        "HasAdvancedConditions": true
+      }
+    ],
+    "tests": []
+  },
+  "name": "Last Round (vs Human)",
+  "key": "43e68bf4-4dc8-4938-a5d7-8f1e17e1202e",
+  "group": "Poker",
+  "description": "This plays during the hand quality phase for the final round of the game, if your player and the human are the only ones still standing."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/4df79a02-26c8-4fa7-b7cd-e8377533ed58.txt b/editor source/SPNATI Character Editor/Resources/Recipes/4df79a02-26c8-4fa7-b7cd-e8377533ed58.txt
new file mode 100644
index 0000000000000000000000000000000000000000..be851ca5568844e4cee47313c746d7465057b5fd
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/4df79a02-26c8-4fa7-b7cd-e8377533ed58.txt	
@@ -0,0 +1,18 @@
+{
+  "case": {
+    "tag": "opponent_lost",
+    "oneShotId": 0,
+    "counters": [],
+    "tests": [
+      {
+        "expr": "~target.lead~",
+        "cmp": ">",
+        "value": "1"
+      }
+    ]
+  },
+  "name": "Leader Loses - Still Leading",
+  "key": "4df79a02-26c8-4fa7-b7cd-e8377533ed58",
+  "group": "Poker",
+  "description": "Must strip phase when a player with the most remaining layers loses but will remain in the lead"
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/5343e2e7-3c56-477d-b248-332021a916e1.txt b/editor source/SPNATI Character Editor/Resources/Recipes/5343e2e7-3c56-477d-b248-332021a916e1.txt
new file mode 100644
index 0000000000000000000000000000000000000000..cf5a40de00e77b1fa7d9219ab822ca18fc4e8b40
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/5343e2e7-3c56-477d-b248-332021a916e1.txt	
@@ -0,0 +1,18 @@
+{
+  "case": {
+    "tag": "opponent_lost",
+    "oneShotId": 0,
+    "counters": [],
+    "tests": [
+      {
+        "expr": "~target.revplace~",
+        "cmp": "==",
+        "value": "1"
+      }
+    ]
+  },
+  "name": "Last Place Loses Hand",
+  "key": "5343e2e7-3c56-477d-b248-332021a916e1",
+  "group": "Poker",
+  "description": "The player with the least remaining layers has lost another hand."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/5eced77a-bd5e-4d35-8a1b-a979a1c73250.txt b/editor source/SPNATI Character Editor/Resources/Recipes/5eced77a-bd5e-4d35-8a1b-a979a1c73250.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2cf245ecc320b29faf9760cfea0e9b50b5d884cc
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/5eced77a-bd5e-4d35-8a1b-a979a1c73250.txt	
@@ -0,0 +1,13 @@
+{
+  "case": {
+    "tag": "opponent_lost",
+    "oneShotId": 0,
+    "totalRounds": "0",
+    "counters": [],
+    "tests": []
+  },
+  "name": "First to Lose - Other",
+  "key": "5eced77a-bd5e-4d35-8a1b-a979a1c73250",
+  "group": "Poker",
+  "description": "Another character lost the first round of the game."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/6091088d-2c85-4d55-a5f3-05bab45351a8.txt b/editor source/SPNATI Character Editor/Resources/Recipes/6091088d-2c85-4d55-a5f3-05bab45351a8.txt
new file mode 100644
index 0000000000000000000000000000000000000000..22193b1e43ab95d4e9305c47e2eb8969ac3c4a0a
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/6091088d-2c85-4d55-a5f3-05bab45351a8.txt	
@@ -0,0 +1,18 @@
+{
+  "case": {
+    "tag": "opponent_stripped",
+    "oneShotId": 0,
+    "counters": [],
+    "tests": [
+      {
+        "expr": "~target.diff(self)~",
+        "cmp": "==",
+        "value": "0"
+      }
+    ]
+  },
+  "name": "All Tied Up",
+  "key": "6091088d-2c85-4d55-a5f3-05bab45351a8",
+  "group": "Stripping",
+  "description": "An opponent has stripped and now has the same number of remaining layers as you"
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/670e7368-4059-4a56-8171-65d18d43ebff.txt b/editor source/SPNATI Character Editor/Resources/Recipes/670e7368-4059-4a56-8171-65d18d43ebff.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ad98392e6c697cd0879735fb96c5454e641d4799
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/670e7368-4059-4a56-8171-65d18d43ebff.txt	
@@ -0,0 +1,20 @@
+{
+  "case": {
+    "lines": [],
+    "tag": "game_over_defeat",
+    "oneShotId": 0,
+    "counters": [],
+    "tests": [
+      {
+        "expr": "~self.place~",
+        "cmp": "==",
+        "value": "2"
+      }
+    ]
+  },
+  "label": "",
+  "name": "Second Place",
+  "key": "670e7368-4059-4a56-8171-65d18d43ebff",
+  "group": "Game Over",
+  "description": "Your character finished in second place."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/70dd1e4f-4d07-4b44-947d-57b494522f40.txt b/editor source/SPNATI Character Editor/Resources/Recipes/70dd1e4f-4d07-4b44-947d-57b494522f40.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b25a326222239fb3e9a322102f7eaa501aaf0b2b
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/70dd1e4f-4d07-4b44-947d-57b494522f40.txt	
@@ -0,0 +1,13 @@
+{
+  "case": {
+    "tag": "opponent_stripped",
+    "oneShotId": 1,
+    "totalExposed": "1",
+    "counters": [],
+    "tests": []
+  },
+  "name": "First Nudity",
+  "key": "70dd1e4f-4d07-4b44-947d-57b494522f40",
+  "group": "Stripping",
+  "description": "Another character is the very first to reveal their chest or crotch"
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/797c82ff-421e-4c29-a950-094bca5a147d.txt b/editor source/SPNATI Character Editor/Resources/Recipes/797c82ff-421e-4c29-a950-094bca5a147d.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fa096ab2d7751281cb7857e2f2706c7616f89c79
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/797c82ff-421e-4c29-a950-094bca5a147d.txt	
@@ -0,0 +1,33 @@
+{
+  "case": {
+    "lines": [],
+    "tag": "hand",
+    "oneShotId": 0,
+    "counters": [
+      {
+        "count": "1-5",
+        "gender": "",
+        "role": "opp",
+        "var": "leader",
+        "HasAdvancedConditions": true
+      }
+    ],
+    "tests": [
+      {
+        "expr": "~leader.place~",
+        "cmp": "==",
+        "value": "1"
+      },
+      {
+        "expr": "~self.place~",
+        "cmp": ">",
+        "value": "1"
+      }
+    ]
+  },
+  "label": "",
+  "name": "Talk to Leader",
+  "key": "797c82ff-421e-4c29-a950-094bca5a147d",
+  "group": "Poker",
+  "description": "Lets you use ~leader~ to talk to the player with the most clothes remaining."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/8338b475-6f72-461a-9cb7-4510428e1297.txt b/editor source/SPNATI Character Editor/Resources/Recipes/8338b475-6f72-461a-9cb7-4510428e1297.txt
new file mode 100644
index 0000000000000000000000000000000000000000..44706514f50cf02a762c35aefb22faf6a5a71c79
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/8338b475-6f72-461a-9cb7-4510428e1297.txt	
@@ -0,0 +1,19 @@
+{
+  "case": {
+    "tag": "opponent_lost",
+    "oneShotId": 0,
+    "consecutiveLosses": "3",
+    "counters": [],
+    "tests": [
+      {
+        "expr": "~target.biggestlead~",
+        "cmp": ">=",
+        "value": "3"
+      }
+    ]
+  },
+  "name": "Downturn in Luck",
+  "key": "8338b475-6f72-461a-9cb7-4510428e1297",
+  "group": "Poker",
+  "description": "An opponent has lost 3 hands in a row after holding a large lead earlier."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/841c009f-617e-47e1-b1dd-214ba5892e88.txt b/editor source/SPNATI Character Editor/Resources/Recipes/841c009f-617e-47e1-b1dd-214ba5892e88.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a85baf601f5acc175fb65531b1e27e6426b22017
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/841c009f-617e-47e1-b1dd-214ba5892e88.txt	
@@ -0,0 +1,22 @@
+{
+  "case": {
+    "tag": "opponent_lost",
+    "oneShotId": 0,
+    "totalMasturbating": "0",
+    "totalFinished": "0",
+    "counters": [
+      {
+        "count": "1",
+        "gender": "",
+        "status": "lost_all",
+        "role": "target",
+        "HasAdvancedConditions": true
+      }
+    ],
+    "tests": []
+  },
+  "name": "First Forfeiter",
+  "key": "841c009f-617e-47e1-b1dd-214ba5892e88",
+  "group": "Forfeit",
+  "description": "Another player has lost all their clothing and is the first one to have to masturbate."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/93fb1799-f582-4f08-a53c-49d5e56f6278.txt b/editor source/SPNATI Character Editor/Resources/Recipes/93fb1799-f582-4f08-a53c-49d5e56f6278.txt
new file mode 100644
index 0000000000000000000000000000000000000000..214c83d67db4bc71d8684a7860c529a4ed01c700
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/93fb1799-f582-4f08-a53c-49d5e56f6278.txt	
@@ -0,0 +1,21 @@
+{
+  "case": {
+    "lines": [],
+    "tag": "game_over_defeat",
+    "oneShotId": 0,
+    "targetStatus": "lost_all",
+    "counters": [],
+    "tests": [
+      {
+        "expr": "~self.place~",
+        "cmp": "==",
+        "value": "2"
+      }
+    ]
+  },
+  "label": "",
+  "name": "Almost Won",
+  "key": "93fb1799-f582-4f08-a53c-49d5e56f6278",
+  "group": "Game Over",
+  "description": "Your character finished in second place and the winner was naked, meaning you were one round away from winning."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/ab63638e-fcc5-44e9-b020-a677785bb36f.txt b/editor source/SPNATI Character Editor/Resources/Recipes/ab63638e-fcc5-44e9-b020-a677785bb36f.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0348cb90908fd0b67e447786fabfe1d66204644a
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/ab63638e-fcc5-44e9-b020-a677785bb36f.txt	
@@ -0,0 +1,13 @@
+{
+  "case": {
+    "tag": "must_strip",
+    "oneShotId": 0,
+    "totalRounds": "0",
+    "counters": [],
+    "tests": []
+  },
+  "name": "First to Lose - Self",
+  "key": "ab63638e-fcc5-44e9-b020-a677785bb36f",
+  "group": "Poker",
+  "description": "Your character is the first to lose a hand in the game."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/b90f559d-7557-4180-989e-63b583b9a82d.txt b/editor source/SPNATI Character Editor/Resources/Recipes/b90f559d-7557-4180-989e-63b583b9a82d.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c43ccd76411c4802c8d686cd67240c2e02afb09a
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/b90f559d-7557-4180-989e-63b583b9a82d.txt	
@@ -0,0 +1,21 @@
+{
+  "case": {
+    "tag": "stripped",
+    "oneShotId": 1,
+    "totalExposed": "1",
+    "counters": [
+      {
+        "count": "0",
+        "gender": "",
+        "status": "exposed",
+        "role": "opp",
+        "HasAdvancedConditions": true
+      }
+    ],
+    "tests": []
+  },
+  "name": "First Nudity - Self",
+  "key": "b90f559d-7557-4180-989e-63b583b9a82d",
+  "group": "Stripping",
+  "description": "Your character is the very first to reveal their chest or crotch"
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/dba5778a-dbed-4079-9052-d204275d6f93.txt b/editor source/SPNATI Character Editor/Resources/Recipes/dba5778a-dbed-4079-9052-d204275d6f93.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8f0486d220980cc5275bbd39042d0a214db71e5a
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/dba5778a-dbed-4079-9052-d204275d6f93.txt	
@@ -0,0 +1,13 @@
+{
+  "case": {
+    "tag": "opponent_lost",
+    "oneShotId": 0,
+    "consecutiveLosses": "3",
+    "counters": [],
+    "tests": []
+  },
+  "name": "Lost 3 in a Row",
+  "key": "dba5778a-dbed-4079-9052-d204275d6f93",
+  "group": "Poker",
+  "description": "An opponent has just lost their 3rd hand in a row."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Recipes/eb570363-dbe2-45bb-ac47-916fc820de42.txt b/editor source/SPNATI Character Editor/Resources/Recipes/eb570363-dbe2-45bb-ac47-916fc820de42.txt
new file mode 100644
index 0000000000000000000000000000000000000000..18b1849b81d535cd956d1b487bf8d23d27c86668
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Recipes/eb570363-dbe2-45bb-ac47-916fc820de42.txt	
@@ -0,0 +1,33 @@
+{
+  "case": {
+    "lines": [],
+    "tag": "hand",
+    "oneShotId": 0,
+    "counters": [
+      {
+        "count": "1-5",
+        "gender": "",
+        "role": "opp",
+        "var": "leader",
+        "HasAdvancedConditions": true
+      }
+    ],
+    "tests": [
+      {
+        "expr": "~leader.lead~",
+        "cmp": ">",
+        "value": "0"
+      },
+      {
+        "expr": "~self.place~",
+        "cmp": ">",
+        "value": "1"
+      }
+    ]
+  },
+  "label": "",
+  "name": "Talk to Leader (No Ties)",
+  "key": "eb570363-dbe2-45bb-ac47-916fc820de42",
+  "group": "Poker",
+  "description": "Lets you use ~leader~ to talk to the player with the most clothes remaining and is not tied with anyone."
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Black Forest.skin b/editor source/SPNATI Character Editor/Resources/Skins/Black Forest.skin
new file mode 100644
index 0000000000000000000000000000000000000000..6eca7ca0fe48a74d5131dfb56773f6f595bb610c
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Black Forest.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "32, 53, 35",
+  "FieldBackColor": "81, 96, 83",
+  "FieldAltBackColor": "90, 107, 92",
+  "FieldDisabledBackColor": "122, 122, 122",
+  "LabelForeColor": "Silver",
+  "PrimaryForeColor": "123, 159, 137",
+  "PrimaryLightForeColor": "0, 62, 4",
+  "SecondaryForeColor": "143, 177, 177",
+  "SecondaryLightForeColor": "72, 104, 104",
+  "Separator": "120, 136, 119",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "206, 0, 0",
+  "GoodForeColor": "60, 198, 45",
+  "BadForeColor": "255, 85, 85",
+  "FocusRectangle": "White",
+  "Gray": "131, 131, 131",
+  "LightGray": "92, 92, 92",
+  "Orange": "255, 186, 91",
+  "Green": "0, 217, 0",
+  "Blue": "129, 179, 254",
+  "Purple": "214, 139, 245",
+  "Pink": "253, 193, 227",
+  "Red": "242, 87, 91",
+  "Group1": "232, 175, 235",
+  "Group2": "102, 190, 255",
+  "Group3": "175, 210, 128",
+  "Group4": "234, 170, 132",
+  "Group5": "White",
+  "AppColors": {
+    "TimelineRow": "40, 51, 41",
+    "TimelineRowAlt": "52, 67, 53",
+    "TimelineFore": "225, 227, 229",
+    "TimelineSelected": "53, 85, 57",
+    "TimelineRowBorder": "86, 116, 82",
+    "TimelineSubRowBorder": "78, 107, 82",
+    "TimelineBar": "230, 87, 87",
+    "PlaybackBar": "19, 206, 94",
+    "WidgetTitleSelected": "84, 107, 85",
+    "WidgetTitle": "5, 39, 13",
+    "WidgetBorder": "35, 50, 37",
+    "WidgetHeaderRow": "60, 76, 61",
+    "WidgetRow": "78, 90, 75",
+    "WidgetRowSelected": "85, 121, 97",
+    "WidgetRepeat": "133, 158, 134",
+    "KeyframeHeader": "252, 245, 114",
+    "Keyframe0": "172, 176, 181",
+    "KeyframeSelected": "White",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Black Forest",
+  "Group": "Dark",
+  "Description": "A mystical trip through a dark forest",
+  "PrimaryColor": {
+    "Normal": "5, 39, 13",
+    "Hover": "67, 93, 73",
+    "Pressed": "3, 29, 9",
+    "Selected": "4, 37, 12",
+    "Disabled": "29, 29, 29",
+    "DisabledSelected": "28, 28, 28",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "3, 29, 9",
+    "BorderHover": "5, 39, 13",
+    "BorderSelected": "2, 27, 8",
+    "BorderDisabled": "22, 22, 22"
+  },
+  "PrimaryLightColor": {
+    "Normal": "23, 77, 53",
+    "Hover": "81, 121, 103",
+    "Pressed": "17, 57, 39",
+    "Selected": "21, 73, 50",
+    "Disabled": "63, 63, 63",
+    "DisabledSelected": "60, 60, 60",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "10, 31, 21",
+    "BorderHover": "23, 77, 53",
+    "BorderSelected": "16, 54, 37",
+    "BorderDisabled": "47, 47, 47"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "0, 13, 1",
+    "Hover": "63, 73, 64",
+    "Pressed": "0, 9, 0",
+    "Selected": "0, 12, 0",
+    "Disabled": "9, 9, 9",
+    "DisabledSelected": "8, 8, 8",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "0, 9, 0",
+    "BorderHover": "0, 13, 1",
+    "BorderSelected": "0, 8, 0",
+    "BorderDisabled": "6, 6, 6"
+  },
+  "SecondaryColor": {
+    "Normal": "81, 115, 115",
+    "Hover": "124, 150, 150",
+    "Pressed": "60, 86, 86",
+    "Selected": "76, 109, 109",
+    "Disabled": "107, 107, 107",
+    "DisabledSelected": "101, 101, 101",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "60, 86, 86",
+    "BorderHover": "81, 115, 115",
+    "BorderSelected": "57, 81, 81",
+    "BorderDisabled": "80, 80, 80"
+  },
+  "SecondaryLightColor": {
+    "Normal": "143, 177, 177",
+    "Hover": "171, 196, 196",
+    "Pressed": "107, 132, 132",
+    "Selected": "135, 168, 168",
+    "Disabled": "DarkGray",
+    "DisabledSelected": "160, 160, 160",
+    "ForeColor": "Black",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "107, 132, 132",
+    "BorderHover": "143, 177, 177",
+    "BorderSelected": "101, 125, 125",
+    "BorderDisabled": "126, 126, 126"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "17, 56, 38",
+    "Hover": "76, 105, 92",
+    "Pressed": "12, 42, 28",
+    "Selected": "16, 53, 36",
+    "Disabled": "46, 46, 46",
+    "DisabledSelected": "43, 43, 43",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "12, 42, 28",
+    "BorderHover": "17, 56, 38",
+    "BorderSelected": "7, 24, 16",
+    "BorderDisabled": "34, 34, 34"
+  },
+  "Surface": {
+    "Normal": "50, 63, 57",
+    "Hover": "101, 111, 106",
+    "Pressed": "37, 47, 42",
+    "Selected": "47, 59, 54",
+    "Disabled": "59, 59, 59",
+    "DisabledSelected": "56, 56, 56",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "108, 155, 115",
+    "BorderHover": "50, 63, 57",
+    "BorderSelected": "35, 44, 39",
+    "BorderDisabled": "44, 44, 44"
+  },
+  "Background": {
+    "Normal": "40, 51, 41",
+    "Hover": "93, 102, 94",
+    "Pressed": "30, 38, 30",
+    "Selected": "38, 48, 38",
+    "Disabled": "47, 47, 47",
+    "DisabledSelected": "45, 45, 45",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "57, 72, 57",
+    "BorderHover": "40, 51, 41",
+    "BorderSelected": "28, 36, 28",
+    "BorderDisabled": "35, 35, 35"
+  },
+  "PrimaryWidget": {
+    "Normal": "119, 179, 145",
+    "Hover": "153, 198, 172",
+    "Pressed": "89, 134, 108",
+    "Selected": "113, 170, 137",
+    "Disabled": "163, 163, 163",
+    "DisabledSelected": "155, 155, 155",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "89, 134, 108",
+    "BorderHover": "119, 179, 145",
+    "BorderSelected": "84, 127, 102",
+    "BorderDisabled": "122, 122, 122"
+  },
+  "SecondaryWidget": {
+    "Normal": "114, 214, 252",
+    "Hover": "149, 224, 252",
+    "Pressed": "85, 160, 189",
+    "Selected": "108, 203, 239",
+    "Disabled": "195, 195, 195",
+    "DisabledSelected": "185, 185, 185",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "85, 160, 189",
+    "BorderHover": "114, 214, 252",
+    "BorderSelected": "80, 152, 179",
+    "BorderDisabled": "146, 146, 146"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Blue.skin b/editor source/SPNATI Character Editor/Resources/Skins/Blue.skin
new file mode 100644
index 0000000000000000000000000000000000000000..0f8ad61c65f873862940dca325f41d19d553e658
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Blue.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "220, 229, 245",
+  "FieldBackColor": "White",
+  "FieldAltBackColor": "237, 241, 254",
+  "FieldDisabledBackColor": "235, 235, 235",
+  "LabelForeColor": "127, 0, 0, 0",
+  "PrimaryForeColor": "0, 34, 123",
+  "PrimaryLightForeColor": "147, 150, 229",
+  "SecondaryForeColor": "108, 185, 247",
+  "SecondaryLightForeColor": "108, 185, 247",
+  "Separator": "179, 218, 251",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "217, 0, 0",
+  "GoodForeColor": "Green",
+  "BadForeColor": "Red",
+  "FocusRectangle": "Black",
+  "Gray": "Gray",
+  "LightGray": "LightGray",
+  "Orange": "243, 99, 12",
+  "Green": "Green",
+  "Blue": "Blue",
+  "Purple": "Purple",
+  "Pink": "234, 85, 164",
+  "Red": "Red",
+  "Group1": "148, 89, 160",
+  "Group2": "0, 98, 173",
+  "Group3": "86, 119, 41",
+  "Group4": "175, 89, 49",
+  "Group5": "Black",
+  "AppColors": {
+    "TimelineRow": "230, 228, 243",
+    "TimelineRowAlt": "241, 242, 245",
+    "TimelineFore": "Black",
+    "TimelineSelected": "232, 233, 253",
+    "TimelineRowBorder": "150, 150, 150",
+    "TimelineSubRowBorder": "170, 170, 170",
+    "TimelineBar": "Red",
+    "PlaybackBar": "Green",
+    "WidgetTitleSelected": "White",
+    "WidgetTitle": "184, 184, 209",
+    "WidgetBorder": "Black",
+    "WidgetHeaderRow": "143, 168, 250",
+    "WidgetRow": "203, 207, 226",
+    "WidgetRowSelected": "223, 226, 236",
+    "WidgetRepeat": "103, 106, 116",
+    "KeyframeHeader": "255, 226, 66",
+    "Keyframe0": "180, 180, 180",
+    "KeyframeSelected": "245, 245, 255",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Blue",
+  "Group": "Light",
+  "Description": "Modern, simple blue",
+  "PrimaryColor": {
+    "Normal": "57, 73, 171",
+    "Hover": "106, 118, 192",
+    "Pressed": "42, 54, 128",
+    "Selected": "54, 69, 162",
+    "Disabled": "76, 76, 76",
+    "DisabledSelected": "72, 72, 72",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "42, 54, 128",
+    "BorderHover": "57, 73, 171",
+    "BorderSelected": "39, 51, 121",
+    "BorderDisabled": "56, 56, 56"
+  },
+  "PrimaryLightColor": {
+    "Normal": "111, 116, 221",
+    "Hover": "147, 150, 229",
+    "Pressed": "83, 87, 165",
+    "Selected": "105, 110, 209",
+    "Disabled": "122, 122, 122",
+    "DisabledSelected": "116, 116, 116",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "83, 87, 165",
+    "BorderHover": "75, 79, 201",
+    "BorderSelected": "78, 82, 156",
+    "BorderDisabled": "91, 91, 91"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "0, 34, 123",
+    "Hover": "63, 89, 156",
+    "Pressed": "0, 25, 92",
+    "Selected": "0, 32, 116",
+    "Disabled": "33, 33, 33",
+    "DisabledSelected": "31, 31, 31",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "0, 25, 92",
+    "BorderHover": "0, 34, 123",
+    "BorderSelected": "0, 23, 87",
+    "BorderDisabled": "24, 24, 24"
+  },
+  "SecondaryColor": {
+    "Normal": "144, 202, 249",
+    "Hover": "171, 215, 250",
+    "Pressed": "108, 151, 186",
+    "Selected": "136, 191, 236",
+    "Disabled": "193, 193, 193",
+    "DisabledSelected": "182, 182, 182",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "108, 151, 186",
+    "BorderHover": "62, 163, 244",
+    "BorderSelected": "102, 143, 176",
+    "BorderDisabled": "144, 144, 144"
+  },
+  "SecondaryLightColor": {
+    "Normal": "179, 253, 255",
+    "Hover": "198, 253, 255",
+    "Pressed": "134, 189, 191",
+    "Selected": "170, 240, 242",
+    "Disabled": "237, 237, 237",
+    "DisabledSelected": "225, 225, 225",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "134, 189, 191",
+    "BorderHover": "0, 183, 187",
+    "BorderSelected": "127, 179, 181",
+    "BorderDisabled": "177, 177, 177"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "93, 153, 198",
+    "Hover": "133, 178, 212",
+    "Pressed": "69, 114, 148",
+    "Selected": "88, 145, 188",
+    "Disabled": "143, 143, 143",
+    "DisabledSelected": "135, 135, 135",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "69, 114, 148",
+    "BorderHover": "93, 153, 198",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "106, 106, 106"
+  },
+  "Surface": {
+    "Normal": "244, 250, 255",
+    "Hover": "220, 220, 239",
+    "Pressed": "169, 185, 205",
+    "Selected": "231, 237, 242",
+    "Disabled": "225, 225, 225",
+    "DisabledSelected": "236, 236, 236",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "165, 182, 209",
+    "BorderHover": "174, 174, 210",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "186, 186, 186"
+  },
+  "Background": {
+    "Normal": "234, 241, 255",
+    "Hover": "202, 219, 255",
+    "Pressed": "149, 163, 217",
+    "Selected": "222, 228, 242",
+    "Disabled": "218, 218, 218",
+    "DisabledSelected": "227, 227, 227",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "175, 180, 191",
+    "BorderHover": "234, 241, 255",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "179, 179, 179"
+  },
+  "PrimaryWidget": {
+    "Normal": "57, 73, 171",
+    "Hover": "106, 118, 192",
+    "Pressed": "42, 54, 128",
+    "Selected": "54, 69, 162",
+    "Disabled": "76, 76, 76",
+    "DisabledSelected": "72, 72, 72",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "42, 54, 128",
+    "BorderHover": "57, 73, 171",
+    "BorderSelected": "39, 51, 121",
+    "BorderDisabled": "56, 56, 56"
+  },
+  "SecondaryWidget": {
+    "Normal": "144, 202, 249",
+    "Hover": "171, 215, 250",
+    "Pressed": "108, 151, 186",
+    "Selected": "136, 191, 236",
+    "Disabled": "193, 193, 193",
+    "DisabledSelected": "182, 182, 182",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "108, 151, 186",
+    "BorderHover": "144, 202, 249",
+    "BorderSelected": "102, 143, 176",
+    "BorderDisabled": "144, 144, 144"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Classic.skin b/editor source/SPNATI Character Editor/Resources/Skins/Classic.skin
new file mode 100644
index 0000000000000000000000000000000000000000..d47a8fdc87b74999c8b9aea4d8d03f073848d72b
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Classic.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "LightGray",
+  "FieldBackColor": "White",
+  "FieldAltBackColor": "247, 247, 247",
+  "FieldDisabledBackColor": "ControlLightLight",
+  "LabelForeColor": "127, 0, 0, 0",
+  "PrimaryForeColor": "0, 18, 147",
+  "PrimaryLightForeColor": "Black",
+  "SecondaryForeColor": "94, 86, 239",
+  "SecondaryLightForeColor": "Black",
+  "Separator": "Gainsboro",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "239, 90, 102",
+  "GoodForeColor": "Green",
+  "BadForeColor": "Red",
+  "FocusRectangle": "Black",
+  "Gray": "Gray",
+  "LightGray": "LightGray",
+  "Orange": "243, 99, 12",
+  "Green": "Green",
+  "Blue": "Blue",
+  "Purple": "Purple",
+  "Pink": "234, 85, 164",
+  "Red": "Red",
+  "Group1": "148, 89, 160",
+  "Group2": "0, 98, 173",
+  "Group3": "86, 119, 41",
+  "Group4": "175, 89, 49",
+  "Group5": "Black",
+  "AppColors": {
+    "TimelineRow": "200, 200, 200",
+    "TimelineRowAlt": "210, 210, 210",
+    "TimelineFore": "Black",
+    "TimelineSelected": "230, 230, 255",
+    "TimelineRowBorder": "150, 150, 150",
+    "TimelineSubRowBorder": "170, 170, 170",
+    "TimelineBar": "Red",
+    "PlaybackBar": "Green",
+    "WidgetTitleSelected": "White",
+    "WidgetTitle": "185, 185, 185",
+    "WidgetBorder": "Black",
+    "WidgetHeaderRow": "153, 197, 255",
+    "WidgetRow": "203, 206, 216",
+    "WidgetRowSelected": "223, 226, 236",
+    "WidgetRepeat": "103, 106, 116",
+    "KeyframeHeader": "255, 226, 66",
+    "Keyframe0": "180, 180, 180",
+    "KeyframeSelected": "245, 245, 255",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Classic",
+  "Group": "Light",
+  "Description": "Inspired by the days of old",
+  "PrimaryColor": {
+    "Normal": "225, 225, 225",
+    "Hover": "208, 237, 253",
+    "Pressed": "169, 216, 250",
+    "Selected": "225, 225, 225",
+    "Disabled": "225, 225, 225",
+    "DisabledSelected": "213, 213, 213",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "168, 168, 168",
+    "BorderHover": "142, 206, 253",
+    "BorderSelected": "95, 121, 245",
+    "BorderDisabled": "168, 168, 168"
+  },
+  "PrimaryLightColor": {
+    "Normal": "244, 244, 244",
+    "Hover": "208, 237, 253",
+    "Pressed": "183, 183, 183",
+    "Selected": "231, 231, 231",
+    "Disabled": "244, 244, 244",
+    "DisabledSelected": "231, 231, 231",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "183, 183, 183",
+    "BorderHover": "124, 203, 250",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "183, 183, 183"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "200, 200, 200",
+    "Hover": "225, 225, 225",
+    "Pressed": "150, 150, 150",
+    "Selected": "190, 190, 190",
+    "Disabled": "200, 200, 200",
+    "DisabledSelected": "190, 190, 190",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "150, 150, 150",
+    "BorderHover": "200, 200, 200",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "150, 150, 150"
+  },
+  "SecondaryColor": {
+    "Normal": "Silver",
+    "Hover": "162, 198, 253",
+    "Pressed": "144, 144, 144",
+    "Selected": "182, 182, 182",
+    "Disabled": "Silver",
+    "DisabledSelected": "182, 182, 182",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "144, 144, 144",
+    "BorderHover": "141, 136, 249",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "144, 144, 144"
+  },
+  "SecondaryLightColor": {
+    "Normal": "230, 230, 230",
+    "Hover": "208, 237, 253",
+    "Pressed": "172, 172, 172",
+    "Selected": "218, 218, 218",
+    "Disabled": "230, 230, 230",
+    "DisabledSelected": "218, 218, 218",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "172, 172, 172",
+    "BorderHover": "230, 230, 230",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "172, 172, 172"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "Gray",
+    "Hover": "159, 159, 159",
+    "Pressed": "96, 96, 96",
+    "Selected": "121, 121, 121",
+    "Disabled": "Gray",
+    "DisabledSelected": "121, 121, 121",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "96, 96, 96",
+    "BorderHover": "Gray",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "96, 96, 96"
+  },
+  "Surface": {
+    "Normal": "240, 240, 240",
+    "Hover": "208, 237, 253",
+    "Pressed": "180, 180, 180",
+    "Selected": "228, 228, 228",
+    "Disabled": "199, 199, 199",
+    "DisabledSelected": "228, 228, 228",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "180, 180, 180",
+    "BorderHover": "170, 205, 249",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "180, 180, 180"
+  },
+  "Background": {
+    "Normal": "240, 240, 240",
+    "Hover": "194, 205, 254",
+    "Pressed": "180, 180, 180",
+    "Selected": "228, 228, 228",
+    "Disabled": "201, 201, 201",
+    "DisabledSelected": "228, 228, 228",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "180, 180, 180",
+    "BorderHover": "240, 240, 240",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "180, 180, 180"
+  },
+  "PrimaryWidget": {
+    "Normal": "Black",
+    "Hover": "63, 63, 63",
+    "Pressed": "Black",
+    "Selected": "Black",
+    "Disabled": "Black",
+    "DisabledSelected": "Black",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "Black",
+    "BorderHover": "Black",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "Black"
+  },
+  "SecondaryWidget": {
+    "Normal": "0, 128, 255",
+    "Hover": "63, 159, 255",
+    "Pressed": "0, 96, 191",
+    "Selected": "0, 121, 242",
+    "Disabled": "109, 109, 109",
+    "DisabledSelected": "104, 104, 104",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "0, 96, 191",
+    "BorderHover": "0, 128, 255",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "82, 82, 82"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Dark Mode.skin b/editor source/SPNATI Character Editor/Resources/Skins/Dark Mode.skin
new file mode 100644
index 0000000000000000000000000000000000000000..c6f936bd027f67a2136a948cddbccc1075229337
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Dark Mode.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "59, 61, 63",
+  "FieldBackColor": "88, 88, 88",
+  "FieldAltBackColor": "100, 100, 100",
+  "FieldDisabledBackColor": "122, 122, 122",
+  "LabelForeColor": "Silver",
+  "PrimaryForeColor": "121, 154, 242",
+  "PrimaryLightForeColor": "51, 153, 255",
+  "SecondaryForeColor": "51, 153, 255",
+  "SecondaryLightForeColor": "51, 153, 255",
+  "Separator": "Gray",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "206, 0, 0",
+  "GoodForeColor": "60, 198, 45",
+  "BadForeColor": "255, 85, 85",
+  "FocusRectangle": "White",
+  "Gray": "131, 131, 131",
+  "LightGray": "92, 92, 92",
+  "Orange": "255, 186, 91",
+  "Green": "0, 217, 0",
+  "Blue": "129, 179, 254",
+  "Purple": "214, 139, 245",
+  "Pink": "253, 193, 227",
+  "Red": "242, 87, 91",
+  "Group1": "232, 175, 235",
+  "Group2": "102, 190, 255",
+  "Group3": "175, 210, 128",
+  "Group4": "234, 170, 132",
+  "Group5": "White",
+  "AppColors": {
+    "TimelineRow": "38, 40, 42",
+    "TimelineRowAlt": "58, 60, 62",
+    "TimelineFore": "225, 227, 229",
+    "TimelineSelected": "35, 57, 103",
+    "TimelineRowBorder": "150, 154, 158",
+    "TimelineSubRowBorder": "88, 92, 97",
+    "TimelineBar": "230, 87, 87",
+    "PlaybackBar": "19, 206, 94",
+    "WidgetTitleSelected": "103, 117, 141",
+    "WidgetTitle": "61, 91, 165",
+    "WidgetBorder": "175, 176, 199",
+    "WidgetHeaderRow": "61, 91, 165",
+    "WidgetRow": "81, 83, 85",
+    "WidgetRowSelected": "89, 102, 117",
+    "WidgetRepeat": "128, 131, 135",
+    "KeyframeHeader": "252, 245, 114",
+    "Keyframe0": "172, 176, 181",
+    "KeyframeSelected": "White",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Dark Mode",
+  "Group": "Dark",
+  "Description": "Light text on a dark background",
+  "PrimaryColor": {
+    "Normal": "48, 50, 52",
+    "Hover": "48, 78, 142",
+    "Pressed": "38, 62, 111",
+    "Selected": "45, 47, 49",
+    "Disabled": "49, 49, 49",
+    "DisabledSelected": "46, 46, 46",
+    "ForeColor": "White",
+    "DisabledForeColor": "154, 154, 154",
+    "Border": "52, 53, 56",
+    "BorderHover": "68, 108, 191",
+    "BorderSelected": "34, 35, 37",
+    "BorderDisabled": "36, 36, 36"
+  },
+  "PrimaryLightColor": {
+    "Normal": "54, 56, 58",
+    "Hover": "63, 103, 186",
+    "Pressed": "40, 42, 43",
+    "Selected": "51, 53, 55",
+    "Disabled": "55, 55, 55",
+    "DisabledSelected": "52, 52, 52",
+    "ForeColor": "White",
+    "DisabledForeColor": "157, 157, 157",
+    "Border": "89, 95, 96",
+    "BorderHover": "70, 109, 191",
+    "BorderSelected": "38, 39, 40",
+    "BorderDisabled": "41, 41, 41"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "33, 35, 37",
+    "Hover": "33, 54, 97",
+    "Pressed": "24, 26, 27",
+    "Selected": "31, 33, 35",
+    "Disabled": "34, 34, 34",
+    "DisabledSelected": "32, 32, 32",
+    "ForeColor": "White",
+    "DisabledForeColor": "106, 108, 110",
+    "Border": "80, 87, 90",
+    "BorderHover": "108, 115, 121",
+    "BorderSelected": "22, 24, 25",
+    "BorderDisabled": "25, 25, 25"
+  },
+  "SecondaryColor": {
+    "Normal": "48, 78, 140",
+    "Hover": "99, 122, 168",
+    "Pressed": "36, 58, 105",
+    "Selected": "45, 74, 133",
+    "Disabled": "76, 76, 76",
+    "DisabledSelected": "72, 72, 72",
+    "ForeColor": "White",
+    "DisabledForeColor": "153, 153, 153",
+    "Border": "36, 58, 105",
+    "BorderHover": "48, 78, 140",
+    "BorderSelected": "34, 55, 99",
+    "BorderDisabled": "56, 56, 56"
+  },
+  "SecondaryLightColor": {
+    "Normal": "123, 159, 242",
+    "Hover": "156, 183, 245",
+    "Pressed": "92, 119, 181",
+    "Selected": "116, 151, 229",
+    "Disabled": "157, 157, 157",
+    "DisabledSelected": "149, 149, 149",
+    "ForeColor": "Black",
+    "DisabledForeColor": "144, 144, 144",
+    "Border": "92, 119, 181",
+    "BorderHover": "123, 159, 242",
+    "BorderSelected": "87, 113, 171",
+    "BorderDisabled": "117, 117, 117"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "0, 103, 206",
+    "Hover": "63, 141, 218",
+    "Pressed": "0, 77, 154",
+    "Selected": "0, 97, 195",
+    "Disabled": "88, 88, 88",
+    "DisabledSelected": "83, 83, 83",
+    "ForeColor": "243, 243, 243",
+    "DisabledForeColor": "148, 148, 148",
+    "Border": "0, 77, 154",
+    "BorderHover": "0, 103, 206",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "66, 66, 66"
+  },
+  "Surface": {
+    "Normal": "66, 66, 66",
+    "Hover": "113, 113, 113",
+    "Pressed": "49, 49, 49",
+    "Selected": "62, 62, 62",
+    "Disabled": "116, 116, 116",
+    "DisabledSelected": "62, 62, 62",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "116, 116, 116",
+    "BorderHover": "72, 72, 72",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "49, 49, 49"
+  },
+  "Background": {
+    "Normal": "42, 42, 42",
+    "Hover": "95, 95, 95",
+    "Pressed": "31, 31, 31",
+    "Selected": "39, 39, 39",
+    "Disabled": "106, 106, 106",
+    "DisabledSelected": "39, 39, 39",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "111, 111, 111",
+    "BorderHover": "148, 148, 148",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "88, 88, 88"
+  },
+  "PrimaryWidget": {
+    "Normal": "73, 117, 218",
+    "Hover": "118, 151, 227",
+    "Pressed": "54, 87, 163",
+    "Selected": "137, 164, 224",
+    "Disabled": "114, 114, 114",
+    "DisabledSelected": "109, 109, 109",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "147, 168, 219",
+    "BorderHover": "73, 117, 218",
+    "BorderSelected": "102, 133, 204",
+    "BorderDisabled": "85, 85, 85"
+  },
+  "SecondaryWidget": {
+    "Normal": "114, 214, 252",
+    "Hover": "149, 224, 252",
+    "Pressed": "85, 160, 189",
+    "Selected": "108, 203, 239",
+    "Disabled": "195, 195, 195",
+    "DisabledSelected": "185, 185, 185",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "85, 160, 189",
+    "BorderHover": "114, 214, 252",
+    "BorderSelected": "80, 152, 179",
+    "BorderDisabled": "146, 146, 146"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Dark Rose.skin b/editor source/SPNATI Character Editor/Resources/Skins/Dark Rose.skin
new file mode 100644
index 0000000000000000000000000000000000000000..2a582846c313a582f233438d591fa6afcd2834bb
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Dark Rose.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "68, 68, 70",
+  "FieldBackColor": "88, 88, 88",
+  "FieldAltBackColor": "100, 100, 100",
+  "FieldDisabledBackColor": "87, 87, 87",
+  "LabelForeColor": "Silver",
+  "PrimaryForeColor": "199, 99, 143",
+  "PrimaryLightForeColor": "141, 52, 92",
+  "SecondaryForeColor": "35, 27, 24",
+  "SecondaryLightForeColor": "106, 79, 75",
+  "Separator": "Gray",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "206, 0, 0",
+  "GoodForeColor": "57, 187, 43",
+  "BadForeColor": "255, 85, 85",
+  "FocusRectangle": "White",
+  "Gray": "131, 131, 131",
+  "LightGray": "92, 92, 92",
+  "Orange": "255, 186, 91",
+  "Green": "0, 217, 0",
+  "Blue": "129, 179, 254",
+  "Purple": "214, 139, 245",
+  "Pink": "253, 193, 227",
+  "Red": "242, 87, 91",
+  "Group1": "232, 175, 235",
+  "Group2": "102, 190, 255",
+  "Group3": "175, 210, 128",
+  "Group4": "234, 170, 132",
+  "Group5": "White",
+  "AppColors": {
+    "TimelineRow": "38, 40, 42",
+    "TimelineRowAlt": "58, 60, 62",
+    "TimelineFore": "225, 227, 229",
+    "TimelineSelected": "108, 57, 66",
+    "TimelineRowBorder": "150, 154, 158",
+    "TimelineSubRowBorder": "88, 92, 97",
+    "TimelineBar": "230, 87, 87",
+    "PlaybackBar": "19, 206, 94",
+    "WidgetTitleSelected": "159, 0, 72",
+    "WidgetTitle": "86, 0, 39",
+    "WidgetBorder": "175, 176, 199",
+    "WidgetHeaderRow": "182, 44, 96",
+    "WidgetRow": "81, 83, 85",
+    "WidgetRowSelected": "89, 102, 117",
+    "WidgetRepeat": "128, 131, 135",
+    "KeyframeHeader": "252, 245, 114",
+    "Keyframe0": "172, 176, 181",
+    "KeyframeSelected": "White",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Dark Rose",
+  "Group": "Dark",
+  "Description": "Flowers of a wilted love",
+  "PrimaryColor": {
+    "Normal": "136, 14, 79",
+    "Hover": "165, 74, 123",
+    "Pressed": "102, 10, 59",
+    "Selected": "129, 13, 75",
+    "Disabled": "44, 44, 44",
+    "DisabledSelected": "42, 42, 42",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "102, 10, 59",
+    "BorderHover": "136, 14, 79",
+    "BorderSelected": "133, 33, 68",
+    "BorderDisabled": "33, 33, 33"
+  },
+  "PrimaryLightColor": {
+    "Normal": "176, 64, 115",
+    "Hover": "195, 111, 150",
+    "Pressed": "132, 48, 86",
+    "Selected": "167, 60, 109",
+    "Disabled": "91, 91, 91",
+    "DisabledSelected": "86, 86, 86",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "132, 48, 86",
+    "BorderHover": "176, 64, 115",
+    "BorderSelected": "125, 45, 81",
+    "BorderDisabled": "68, 68, 68"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "86, 0, 39",
+    "Hover": "128, 63, 93",
+    "Pressed": "64, 0, 29",
+    "Selected": "81, 0, 37",
+    "Disabled": "21, 21, 21",
+    "DisabledSelected": "19, 19, 19",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "64, 0, 29",
+    "BorderHover": "86, 0, 39",
+    "BorderSelected": "60, 0, 27",
+    "BorderDisabled": "15, 15, 15"
+  },
+  "SecondaryColor": {
+    "Normal": "122, 78, 69",
+    "Hover": "155, 122, 115",
+    "Pressed": "91, 58, 51",
+    "Selected": "115, 74, 65",
+    "Disabled": "86, 86, 86",
+    "DisabledSelected": "82, 82, 82",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "91, 58, 51",
+    "BorderHover": "122, 78, 69",
+    "BorderSelected": "154, 98, 86",
+    "BorderDisabled": "64, 64, 64"
+  },
+  "SecondaryLightColor": {
+    "Normal": "182, 156, 152",
+    "Hover": "200, 180, 177",
+    "Pressed": "136, 117, 114",
+    "Selected": "172, 148, 144",
+    "Disabled": "161, 161, 161",
+    "DisabledSelected": "152, 152, 152",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "136, 117, 114",
+    "BorderHover": "182, 156, 152",
+    "BorderSelected": "129, 111, 108",
+    "BorderDisabled": "120, 120, 120"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "27, 0, 0",
+    "Hover": "84, 63, 63",
+    "Pressed": "20, 0, 0",
+    "Selected": "25, 0, 0",
+    "Disabled": "5, 5, 5",
+    "DisabledSelected": "5, 5, 5",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "20, 0, 0",
+    "BorderHover": "27, 0, 0",
+    "BorderSelected": "19, 0, 0",
+    "BorderDisabled": "4, 4, 4"
+  },
+  "Surface": {
+    "Normal": "66, 66, 66",
+    "Hover": "113, 113, 113",
+    "Pressed": "49, 49, 49",
+    "Selected": "62, 62, 62",
+    "Disabled": "133, 133, 133",
+    "DisabledSelected": "62, 62, 62",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "117, 117, 117",
+    "BorderHover": "72, 72, 72",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "49, 49, 49"
+  },
+  "Background": {
+    "Normal": "35, 35, 35",
+    "Hover": "90, 90, 90",
+    "Pressed": "26, 26, 26",
+    "Selected": "33, 33, 33",
+    "Disabled": "100, 100, 100",
+    "DisabledSelected": "33, 33, 33",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "83, 83, 83",
+    "BorderHover": "35, 35, 35",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "26, 26, 26"
+  },
+  "PrimaryWidget": {
+    "Normal": "208, 21, 119",
+    "Hover": "219, 79, 153",
+    "Pressed": "156, 15, 89",
+    "Selected": "160, 16, 91",
+    "Disabled": "67, 67, 67",
+    "DisabledSelected": "63, 63, 63",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "240, 96, 139",
+    "BorderHover": "208, 21, 119",
+    "BorderSelected": "187, 17, 106",
+    "BorderDisabled": "50, 50, 50"
+  },
+  "SecondaryWidget": {
+    "Normal": "122, 78, 69",
+    "Hover": "155, 122, 115",
+    "Pressed": "91, 58, 51",
+    "Selected": "115, 74, 65",
+    "Disabled": "86, 86, 86",
+    "DisabledSelected": "82, 82, 82",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "91, 58, 51",
+    "BorderHover": "122, 78, 69",
+    "BorderSelected": "86, 55, 48",
+    "BorderDisabled": "64, 64, 64"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Deep Sea.skin b/editor source/SPNATI Character Editor/Resources/Skins/Deep Sea.skin
new file mode 100644
index 0000000000000000000000000000000000000000..6ace7325089ddf8c221792ddbb4e5b183a48a291
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Deep Sea.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "71, 75, 97",
+  "FieldBackColor": "88, 88, 88",
+  "FieldAltBackColor": "100, 100, 100",
+  "FieldDisabledBackColor": "87, 87, 87",
+  "LabelForeColor": "Silver",
+  "PrimaryForeColor": "194, 199, 237",
+  "PrimaryLightForeColor": "84, 114, 211",
+  "SecondaryForeColor": "79, 179, 191",
+  "SecondaryLightForeColor": "79, 179, 191",
+  "Separator": "99, 99, 156",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "DarkRed",
+  "GoodForeColor": "57, 187, 43",
+  "BadForeColor": "255, 85, 85",
+  "FocusRectangle": "White",
+  "Gray": "131, 131, 131",
+  "LightGray": "92, 92, 92",
+  "Orange": "255, 186, 91",
+  "Green": "0, 217, 0",
+  "Blue": "129, 179, 254",
+  "Purple": "214, 139, 245",
+  "Pink": "253, 193, 227",
+  "Red": "242, 87, 91",
+  "Group1": "232, 175, 235",
+  "Group2": "102, 190, 255",
+  "Group3": "175, 210, 128",
+  "Group4": "234, 170, 132",
+  "Group5": "White",
+  "AppColors": {
+    "TimelineRow": "38, 40, 42",
+    "TimelineRowAlt": "58, 60, 62",
+    "TimelineFore": "225, 227, 229",
+    "TimelineSelected": "35, 57, 103",
+    "TimelineRowBorder": "150, 154, 158",
+    "TimelineSubRowBorder": "88, 92, 97",
+    "TimelineBar": "230, 87, 87",
+    "PlaybackBar": "19, 206, 94",
+    "WidgetTitleSelected": "103, 117, 141",
+    "WidgetTitle": "61, 91, 165",
+    "WidgetBorder": "175, 176, 199",
+    "WidgetHeaderRow": "61, 91, 165",
+    "WidgetRow": "81, 83, 85",
+    "WidgetRowSelected": "89, 102, 117",
+    "WidgetRepeat": "128, 131, 135",
+    "KeyframeHeader": "252, 245, 114",
+    "Keyframe0": "172, 176, 181",
+    "KeyframeSelected": "White",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Deep Sea",
+  "Group": "Dark",
+  "Description": "Deep blues from the ocean",
+  "PrimaryColor": {
+    "Normal": "11, 63, 140",
+    "Hover": "72, 111, 168",
+    "Pressed": "8, 47, 105",
+    "Selected": "10, 59, 133",
+    "Disabled": "57, 57, 57",
+    "DisabledSelected": "53, 53, 53",
+    "ForeColor": "224, 224, 230",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "8, 47, 105",
+    "BorderHover": "11, 63, 140",
+    "BorderSelected": "7, 44, 99",
+    "BorderDisabled": "42, 42, 42"
+  },
+  "PrimaryLightColor": {
+    "Normal": "49, 85, 191",
+    "Hover": "100, 127, 207",
+    "Pressed": "36, 63, 143",
+    "Selected": "46, 80, 181",
+    "Disabled": "84, 84, 84",
+    "DisabledSelected": "80, 80, 80",
+    "ForeColor": "224, 224, 230",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "36, 63, 143",
+    "BorderHover": "49, 85, 191",
+    "BorderSelected": "34, 59, 135",
+    "BorderDisabled": "63, 63, 63"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "0, 21, 70",
+    "Hover": "63, 79, 116",
+    "Pressed": "0, 15, 52",
+    "Selected": "0, 19, 66",
+    "Disabled": "20, 20, 20",
+    "DisabledSelected": "18, 18, 18",
+    "ForeColor": "224, 224, 230",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "0, 15, 52",
+    "BorderHover": "0, 21, 70",
+    "BorderSelected": "0, 14, 49",
+    "BorderDisabled": "14, 14, 14"
+  },
+  "SecondaryColor": {
+    "Normal": "0, 131, 143",
+    "Hover": "63, 162, 171",
+    "Pressed": "0, 98, 107",
+    "Selected": "0, 124, 135",
+    "Disabled": "104, 104, 104",
+    "DisabledSelected": "98, 98, 98",
+    "ForeColor": "220, 220, 234",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "0, 136, 147",
+    "BorderHover": "0, 131, 143",
+    "BorderSelected": "0, 93, 101",
+    "BorderDisabled": "77, 77, 77"
+  },
+  "SecondaryLightColor": {
+    "Normal": "79, 179, 191",
+    "Hover": "123, 198, 207",
+    "Pressed": "59, 134, 143",
+    "Selected": "75, 170, 181",
+    "Disabled": "158, 158, 158",
+    "DisabledSelected": "150, 150, 150",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "59, 134, 143",
+    "BorderHover": "79, 179, 191",
+    "BorderSelected": "56, 127, 135",
+    "BorderDisabled": "118, 118, 118"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "0, 86, 98",
+    "Hover": "63, 128, 137",
+    "Pressed": "0, 64, 73",
+    "Selected": "0, 81, 93",
+    "Disabled": "68, 68, 68",
+    "DisabledSelected": "64, 64, 64",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "0, 64, 73",
+    "BorderHover": "0, 86, 98",
+    "BorderSelected": "0, 60, 69",
+    "BorderDisabled": "51, 51, 51"
+  },
+  "Surface": {
+    "Normal": "78, 78, 78",
+    "Hover": "122, 122, 122",
+    "Pressed": "58, 58, 58",
+    "Selected": "74, 74, 74",
+    "Disabled": "130, 130, 130",
+    "DisabledSelected": "74, 74, 74",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "155, 155, 155",
+    "BorderHover": "58, 58, 58",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "58, 58, 58"
+  },
+  "Background": {
+    "Normal": "32, 31, 39",
+    "Hover": "87, 87, 93",
+    "Pressed": "24, 23, 29",
+    "Selected": "30, 29, 37",
+    "Disabled": "DimGray",
+    "DisabledSelected": "29, 29, 29",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "89, 79, 119",
+    "BorderHover": "32, 31, 39",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "23, 23, 23"
+  },
+  "PrimaryWidget": {
+    "Normal": "98, 125, 236",
+    "Hover": "137, 157, 240",
+    "Pressed": "73, 93, 177",
+    "Selected": "93, 118, 224",
+    "Disabled": "127, 127, 127",
+    "DisabledSelected": "120, 120, 120",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "139, 146, 207",
+    "BorderHover": "98, 125, 236",
+    "BorderSelected": "172, 182, 221",
+    "BorderDisabled": "94, 94, 94"
+  },
+  "SecondaryWidget": {
+    "Normal": "0, 131, 143",
+    "Hover": "63, 162, 171",
+    "Pressed": "0, 98, 107",
+    "Selected": "0, 124, 135",
+    "Disabled": "104, 104, 104",
+    "DisabledSelected": "98, 98, 98",
+    "ForeColor": "220, 220, 234",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "0, 98, 107",
+    "BorderHover": "0, 131, 143",
+    "BorderSelected": "0, 93, 101",
+    "BorderDisabled": "77, 77, 77"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Florina.skin b/editor source/SPNATI Character Editor/Resources/Skins/Florina.skin
new file mode 100644
index 0000000000000000000000000000000000000000..8ffe22b0403ddd4ad7b4f28bc7938630d83d1304
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Florina.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "220, 229, 245",
+  "FieldBackColor": "White",
+  "FieldAltBackColor": "247, 240, 247",
+  "FieldDisabledBackColor": "238, 238, 238",
+  "LabelForeColor": "127, 0, 0, 0",
+  "PrimaryForeColor": "34, 73, 146",
+  "PrimaryLightForeColor": "122, 158, 224",
+  "SecondaryForeColor": "128, 101, 141",
+  "SecondaryLightForeColor": "221, 191, 214",
+  "Separator": "164, 173, 236",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "217, 0, 0",
+  "GoodForeColor": "Green",
+  "BadForeColor": "Red",
+  "FocusRectangle": "Black",
+  "Gray": "Gray",
+  "LightGray": "LightGray",
+  "Orange": "243, 99, 12",
+  "Green": "Green",
+  "Blue": "Blue",
+  "Purple": "Purple",
+  "Pink": "234, 85, 164",
+  "Red": "Red",
+  "Group1": "148, 89, 160",
+  "Group2": "0, 98, 173",
+  "Group3": "86, 119, 41",
+  "Group4": "175, 89, 49",
+  "Group5": "Black",
+  "AppColors": {
+    "TimelineRow": "235, 235, 245",
+    "TimelineRowAlt": "245, 245, 255",
+    "TimelineFore": "Black",
+    "TimelineSelected": "230, 230, 255",
+    "TimelineRowBorder": "150, 150, 150",
+    "TimelineSubRowBorder": "170, 170, 170",
+    "TimelineBar": "Red",
+    "PlaybackBar": "Green",
+    "WidgetTitleSelected": "White",
+    "WidgetTitle": "189, 174, 196",
+    "WidgetBorder": "Black",
+    "WidgetHeaderRow": "226, 197, 226",
+    "WidgetRow": "203, 206, 216",
+    "WidgetRowSelected": "223, 226, 236",
+    "WidgetRepeat": "103, 106, 116",
+    "KeyframeHeader": "255, 226, 66",
+    "Keyframe0": "180, 180, 180",
+    "KeyframeSelected": "245, 245, 255",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Florina",
+  "Group": "Light",
+  "Description": "Design by the Lovely Flier",
+  "PrimaryColor": {
+    "Normal": "57, 73, 171",
+    "Hover": "106, 118, 192",
+    "Pressed": "42, 54, 128",
+    "Selected": "54, 69, 162",
+    "Disabled": "76, 76, 76",
+    "DisabledSelected": "72, 72, 72",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "42, 54, 128",
+    "BorderHover": "57, 73, 171",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "56, 56, 56"
+  },
+  "PrimaryLightColor": {
+    "Normal": "82, 109, 160",
+    "Hover": "125, 145, 183",
+    "Pressed": "61, 81, 120",
+    "Selected": "77, 103, 152",
+    "Disabled": "106, 106, 106",
+    "DisabledSelected": "101, 101, 101",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "61, 81, 120",
+    "BorderHover": "82, 109, 160",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "79, 79, 79"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "49, 49, 106",
+    "Hover": "100, 100, 143",
+    "Pressed": "36, 36, 79",
+    "Selected": "46, 46, 100",
+    "Disabled": "53, 53, 53",
+    "DisabledSelected": "49, 49, 49",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "36, 36, 79",
+    "BorderHover": "49, 49, 106",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "39, 39, 39"
+  },
+  "SecondaryColor": {
+    "Normal": "221, 191, 214",
+    "Hover": "234, 217, 230",
+    "Pressed": "165, 143, 160",
+    "Selected": "209, 181, 203",
+    "Disabled": "199, 199, 199",
+    "DisabledSelected": "188, 188, 188",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "165, 143, 160",
+    "BorderHover": "221, 191, 214",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "148, 148, 148"
+  },
+  "SecondaryLightColor": {
+    "Normal": "235, 214, 235",
+    "Hover": "240, 224, 240",
+    "Pressed": "176, 160, 176",
+    "Selected": "223, 203, 223",
+    "Disabled": "219, 219, 219",
+    "DisabledSelected": "208, 208, 208",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "176, 160, 176",
+    "BorderHover": "226, 194, 226",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "164, 164, 164"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "118, 93, 131",
+    "Hover": "152, 133, 162",
+    "Pressed": "88, 69, 98",
+    "Selected": "112, 88, 124",
+    "Disabled": "101, 101, 101",
+    "DisabledSelected": "95, 95, 95",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "88, 69, 98",
+    "BorderHover": "118, 93, 131",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "75, 75, 75"
+  },
+  "Surface": {
+    "Normal": "WhiteSmoke",
+    "Hover": "White",
+    "Pressed": "183, 183, 183",
+    "Selected": "232, 232, 232",
+    "Disabled": "Silver",
+    "DisabledSelected": "232, 232, 232",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "183, 183, 183",
+    "BorderHover": "212, 212, 212",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "183, 183, 183"
+  },
+  "Background": {
+    "Normal": "235, 235, 245",
+    "Hover": "251, 251, 253",
+    "Pressed": "176, 176, 183",
+    "Selected": "223, 223, 232",
+    "Disabled": "187, 187, 187",
+    "DisabledSelected": "223, 223, 223",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "176, 176, 183",
+    "BorderHover": "167, 167, 209",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "176, 176, 176"
+  },
+  "PrimaryWidget": {
+    "Normal": "57, 73, 171",
+    "Hover": "106, 118, 192",
+    "Pressed": "42, 54, 128",
+    "Selected": "54, 69, 162",
+    "Disabled": "76, 76, 76",
+    "DisabledSelected": "72, 72, 72",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "42, 54, 128",
+    "BorderHover": "57, 73, 171",
+    "BorderSelected": "39, 51, 121",
+    "BorderDisabled": "56, 56, 56"
+  },
+  "SecondaryWidget": {
+    "Normal": "202, 155, 191",
+    "Hover": "215, 180, 207",
+    "Pressed": "151, 116, 143",
+    "Selected": "191, 147, 181",
+    "Disabled": "167, 167, 167",
+    "DisabledSelected": "158, 158, 158",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "151, 116, 143",
+    "BorderHover": "202, 155, 191",
+    "BorderSelected": "143, 110, 135",
+    "BorderDisabled": "125, 125, 125"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Forest.skin b/editor source/SPNATI Character Editor/Resources/Skins/Forest.skin
new file mode 100644
index 0000000000000000000000000000000000000000..a993a69a7a9f5a1b6aecc12c62dd1db01c38300f
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Forest.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "200, 221, 198",
+  "FieldBackColor": "White",
+  "FieldAltBackColor": "242, 250, 235",
+  "FieldDisabledBackColor": "ControlLightLight",
+  "LabelForeColor": "127, 0, 0, 0",
+  "PrimaryForeColor": "0, 97, 13",
+  "PrimaryLightForeColor": "Blue",
+  "SecondaryForeColor": "164, 91, 99",
+  "SecondaryLightForeColor": "250, 232, 229",
+  "Separator": "132, 198, 147",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "DarkRed",
+  "GoodForeColor": "Green",
+  "BadForeColor": "Red",
+  "FocusRectangle": "Black",
+  "Gray": "Gray",
+  "LightGray": "LightGray",
+  "Orange": "243, 99, 12",
+  "Green": "Green",
+  "Blue": "Blue",
+  "Purple": "Purple",
+  "Pink": "234, 85, 164",
+  "Red": "Red",
+  "Group1": "148, 89, 160",
+  "Group2": "0, 98, 173",
+  "Group3": "86, 119, 41",
+  "Group4": "175, 89, 49",
+  "Group5": "Black",
+  "AppColors": {
+    "TimelineRow": "224, 233, 227",
+    "TimelineRowAlt": "240, 244, 241",
+    "TimelineFore": "Black",
+    "TimelineSelected": "241, 245, 242",
+    "TimelineRowBorder": "150, 150, 150",
+    "TimelineSubRowBorder": "170, 170, 170",
+    "TimelineBar": "Red",
+    "PlaybackBar": "Green",
+    "WidgetTitleSelected": "250, 232, 229",
+    "WidgetTitle": "163, 207, 182",
+    "WidgetBorder": "Black",
+    "WidgetHeaderRow": "243, 208, 207",
+    "WidgetRow": "182, 209, 177",
+    "WidgetRowSelected": "247, 232, 232",
+    "WidgetRepeat": "103, 106, 116",
+    "KeyframeHeader": "255, 226, 66",
+    "Keyframe0": "180, 180, 180",
+    "KeyframeSelected": "245, 245, 255",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Forest",
+  "Group": "Light",
+  "Description": "A peaceful walk through a sun-tinged forest",
+  "PrimaryColor": {
+    "Normal": "26, 128, 92",
+    "Hover": "83, 159, 132",
+    "Pressed": "19, 96, 69",
+    "Selected": "24, 121, 87",
+    "Disabled": "103, 103, 103",
+    "DisabledSelected": "97, 97, 97",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "19, 96, 69",
+    "BorderHover": "26, 128, 92",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "77, 77, 77"
+  },
+  "PrimaryLightColor": {
+    "Normal": "89, 188, 156",
+    "Hover": "130, 204, 180",
+    "Pressed": "66, 141, 117",
+    "Selected": "84, 178, 148",
+    "Disabled": "164, 164, 164",
+    "DisabledSelected": "155, 155, 155",
+    "ForeColor": "Black",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "66, 141, 117",
+    "BorderHover": "89, 188, 156",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "123, 123, 123"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "0, 97, 13",
+    "Hover": "63, 136, 73",
+    "Pressed": "0, 72, 9",
+    "Selected": "0, 92, 12",
+    "Disabled": "70, 70, 70",
+    "DisabledSelected": "66, 66, 66",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "0, 72, 9",
+    "BorderHover": "0, 97, 13",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "52, 52, 52"
+  },
+  "SecondaryColor": {
+    "Normal": "131, 79, 79",
+    "Hover": "162, 123, 123",
+    "Pressed": "98, 59, 59",
+    "Selected": "124, 75, 75",
+    "Disabled": "90, 90, 90",
+    "DisabledSelected": "85, 85, 85",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "98, 59, 59",
+    "BorderHover": "131, 79, 79",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "67, 67, 67"
+  },
+  "SecondaryLightColor": {
+    "Normal": "226, 200, 199",
+    "Hover": "233, 213, 213",
+    "Pressed": "169, 150, 149",
+    "Selected": "214, 190, 189",
+    "Disabled": "205, 205, 205",
+    "DisabledSelected": "195, 195, 195",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "169, 150, 149",
+    "BorderHover": "197, 145, 143",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "153, 153, 153"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "85, 47, 51",
+    "Hover": "127, 99, 102",
+    "Pressed": "63, 35, 38",
+    "Selected": "80, 44, 48",
+    "Disabled": "55, 55, 55",
+    "DisabledSelected": "51, 51, 51",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "63, 35, 38",
+    "BorderHover": "85, 47, 51",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "41, 41, 41"
+  },
+  "Surface": {
+    "Normal": "250, 232, 229",
+    "Hover": "251, 237, 235",
+    "Pressed": "187, 174, 171",
+    "Selected": "237, 220, 217",
+    "Disabled": "Silver",
+    "DisabledSelected": "223, 223, 223",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "187, 174, 171",
+    "BorderHover": "233, 150, 135",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "176, 176, 176"
+  },
+  "Background": {
+    "Normal": "224, 233, 227",
+    "Hover": "243, 248, 245",
+    "Pressed": "168, 174, 170",
+    "Selected": "212, 221, 215",
+    "Disabled": "183, 183, 183",
+    "DisabledSelected": "218, 218, 218",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "168, 174, 170",
+    "BorderHover": "224, 233, 227",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "172, 172, 172"
+  },
+  "PrimaryWidget": {
+    "Normal": "26, 128, 92",
+    "Hover": "83, 159, 132",
+    "Pressed": "19, 96, 69",
+    "Selected": "24, 121, 87",
+    "Disabled": "103, 103, 103",
+    "DisabledSelected": "97, 97, 97",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "19, 96, 69",
+    "BorderHover": "26, 128, 92",
+    "BorderSelected": "18, 91, 65",
+    "BorderDisabled": "77, 77, 77"
+  },
+  "SecondaryWidget": {
+    "Normal": "131, 79, 79",
+    "Hover": "162, 123, 123",
+    "Pressed": "98, 59, 59",
+    "Selected": "124, 75, 75",
+    "Disabled": "90, 90, 90",
+    "DisabledSelected": "85, 85, 85",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "98, 59, 59",
+    "BorderHover": "131, 79, 79",
+    "BorderSelected": "93, 56, 56",
+    "BorderDisabled": "67, 67, 67"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Mandevilla.skin b/editor source/SPNATI Character Editor/Resources/Skins/Mandevilla.skin
new file mode 100644
index 0000000000000000000000000000000000000000..87c337e796a0c7b47076c3293bdafdab754ff888
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Mandevilla.skin	
@@ -0,0 +1,193 @@
+{
+  "SurfaceShadowColor": "241, 198, 249",
+  "FieldBackColor": "White",
+  "FieldAltBackColor": "255, 234, 245",
+  "FieldDisabledBackColor": "White",
+  "LabelForeColor": "127, 0, 0, 0",
+  "PrimaryForeColor": "236, 64, 122",
+  "PrimaryLightForeColor": "255, 147, 187",
+  "SecondaryForeColor": "175, 142, 181",
+  "SecondaryLightForeColor": "255, 241, 255",
+  "Separator": "230, 153, 215",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "245, 3, 64",
+  "GoodForeColor": "Green",
+  "BadForeColor": "Red",
+  "FocusRectangle": "Black",
+  "Gray": "Gray",
+  "LightGray": "LightGray",
+  "Orange": "243, 99, 12",
+  "Green": "Green",
+  "Blue": "Blue",
+  "Purple": "Purple",
+  "Pink": "234, 85, 164",
+  "Red": "Red",
+  "Group1": "148, 89, 160",
+  "Group2": "0, 98, 173",
+  "Group3": "86, 119, 41",
+  "Group4": "175, 89, 49",
+  "Group5": "Black",
+  "AppColors": {
+    "TimelineRow": "251, 200, 251",
+    "TimelineRowAlt": "253, 232, 253",
+    "TimelineFore": "Black",
+    "TimelineSelected": "254, 241, 254",
+    "WidgetTitleSelected": "White",
+    "WidgetTitle": "220, 152, 220",
+    "TimelineBar": "Red",
+    "PlaybackBar": "Green",
+    "WidgetBorder": "188, 5, 92",
+    "WidgetHeaderRow": "248, 158, 215",
+    "WidgetRow": "235, 180, 216",
+    "WidgetRowSelected": "255, 223, 239",
+    "KeyframeHeader": "255, 226, 66",
+    "Keyframe0": "200, 180, 180",
+    "KeyframeSelected": "255, 245, 245",
+    "WidgetRepeat": "116, 106, 103",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Mandevilla",
+  "Group": "Light",
+  "Description": "Shades of pink flowers",
+  "PrimaryColor": {
+    "Normal": "236, 64, 122",
+    "Hover": "240, 111, 155",
+    "Pressed": "177, 48, 91",
+    "Selected": "224, 60, 115",
+    "Disabled": "104, 104, 104",
+    "DisabledSelected": "98, 98, 98",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "177, 48, 91",
+    "BorderHover": "236, 64, 122",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "78, 78, 78"
+  },
+  "PrimaryLightColor": {
+    "Normal": "255, 119, 169",
+    "Hover": "255, 166, 200",
+    "Pressed": "191, 89, 126",
+    "Selected": "242, 113, 160",
+    "Disabled": "151, 151, 151",
+    "DisabledSelected": "143, 143, 143",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "191, 89, 126",
+    "BorderHover": "255, 119, 169",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "113, 113, 113"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "180, 0, 78",
+    "Hover": "198, 63, 122",
+    "Pressed": "135, 0, 58",
+    "Selected": "171, 0, 74",
+    "Disabled": "43, 43, 43",
+    "DisabledSelected": "41, 41, 41",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "135, 0, 58",
+    "BorderHover": "180, 0, 78",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "32, 32, 32"
+  },
+  "SecondaryColor": {
+    "Normal": "221, 186, 235",
+    "Hover": "233, 210, 242",
+    "Pressed": "165, 139, 176",
+    "Selected": "209, 176, 223",
+    "Disabled": "196, 196, 196",
+    "DisabledSelected": "186, 186, 186",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "165, 139, 176",
+    "BorderHover": "221, 186, 235",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "147, 147, 147"
+  },
+  "SecondaryLightColor": {
+    "Normal": "255, 241, 255",
+    "Hover": "255, 210, 255",
+    "Pressed": "255, 185, 255",
+    "Selected": "242, 228, 242",
+    "Disabled": "244, 244, 244",
+    "DisabledSelected": "231, 231, 231",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "208, 158, 214",
+    "BorderHover": "219, 155, 227",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "183, 183, 183"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "175, 142, 181",
+    "Hover": "195, 170, 199",
+    "Pressed": "131, 106, 135",
+    "Selected": "166, 134, 171",
+    "Disabled": "151, 151, 151",
+    "DisabledSelected": "143, 143, 143",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "131, 106, 135",
+    "BorderHover": "175, 142, 181",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "113, 113, 113"
+  },
+  "Surface": {
+    "Normal": "255, 250, 255",
+    "Hover": "255, 236, 252",
+    "Pressed": "191, 187, 191",
+    "Selected": "242, 237, 242",
+    "Disabled": "200, 200, 200",
+    "DisabledSelected": "238, 238, 238",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "191, 187, 191",
+    "BorderHover": "255, 191, 255",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "188, 188, 188"
+  },
+  "Background": {
+    "Normal": "252, 220, 252",
+    "Hover": "252, 228, 252",
+    "Pressed": "189, 165, 189",
+    "Selected": "239, 209, 239",
+    "Disabled": "197, 197, 197",
+    "DisabledSelected": "217, 217, 217",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "189, 165, 189",
+    "BorderHover": "252, 220, 252",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "171, 171, 171"
+  },
+  "PrimaryWidget": {
+    "Normal": "236, 64, 122",
+    "Hover": "240, 111, 155",
+    "Pressed": "177, 48, 91",
+    "Selected": "224, 60, 115",
+    "Disabled": "104, 104, 104",
+    "DisabledSelected": "98, 98, 98",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "177, 48, 91",
+    "BorderHover": "236, 64, 122",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "78, 78, 78"
+  },
+  "SecondaryWidget": {
+    "Normal": "199, 153, 219",
+    "Hover": "213, 178, 228",
+    "Pressed": "149, 114, 164",
+    "Selected": "189, 145, 208",
+    "Disabled": "167, 167, 167",
+    "DisabledSelected": "158, 158, 158",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "149, 114, 164",
+    "BorderHover": "199, 153, 219",
+    "BorderSelected": "Gray",
+    "BorderDisabled": "125, 125, 125"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Mint Chocolate.skin b/editor source/SPNATI Character Editor/Resources/Skins/Mint Chocolate.skin
new file mode 100644
index 0000000000000000000000000000000000000000..6902fe0d54ce70706d0f67291ddad52747c90e7d
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Mint Chocolate.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "200, 232, 207",
+  "FieldBackColor": "White",
+  "FieldAltBackColor": "250, 241, 237",
+  "FieldDisabledBackColor": "218, 218, 218",
+  "LabelForeColor": "127, 0, 0, 0",
+  "PrimaryForeColor": "30, 86, 49",
+  "PrimaryLightForeColor": "160, 220, 182",
+  "SecondaryForeColor": "95, 61, 44",
+  "SecondaryLightForeColor": "163, 135, 117",
+  "Separator": "186, 160, 160",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "217, 0, 0",
+  "GoodForeColor": "Green",
+  "BadForeColor": "Red",
+  "FocusRectangle": "Black",
+  "Gray": "Gray",
+  "LightGray": "LightGray",
+  "Orange": "243, 99, 12",
+  "Green": "Green",
+  "Blue": "Blue",
+  "Purple": "Purple",
+  "Pink": "234, 85, 164",
+  "Red": "Red",
+  "Group1": "148, 89, 160",
+  "Group2": "0, 98, 173",
+  "Group3": "86, 119, 41",
+  "Group4": "175, 89, 49",
+  "Group5": "Black",
+  "AppColors": {
+    "TimelineRow": "234, 255, 237",
+    "TimelineRowAlt": "244, 255, 245",
+    "TimelineFore": "Black",
+    "TimelineSelected": "188, 241, 200",
+    "TimelineRowBorder": "116, 184, 136",
+    "TimelineSubRowBorder": "152, 188, 157",
+    "TimelineBar": "Red",
+    "PlaybackBar": "Green",
+    "WidgetTitleSelected": "White",
+    "WidgetTitle": "131, 224, 158",
+    "WidgetBorder": "Black",
+    "WidgetHeaderRow": "216, 187, 177",
+    "WidgetRow": "223, 208, 206",
+    "WidgetRowSelected": "234, 227, 225",
+    "WidgetRepeat": "103, 106, 116",
+    "KeyframeHeader": "255, 226, 66",
+    "Keyframe0": "180, 180, 180",
+    "KeyframeSelected": "245, 245, 255",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Mint Chocolate",
+  "Group": "Light",
+  "Description": "Refreshing mint ice cream with chocolate chips",
+  "PrimaryColor": {
+    "Normal": "49, 142, 82",
+    "Hover": "100, 170, 125",
+    "Pressed": "36, 106, 61",
+    "Selected": "46, 134, 77",
+    "Disabled": "117, 117, 117",
+    "DisabledSelected": "111, 111, 111",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "36, 106, 61",
+    "BorderHover": "49, 142, 82",
+    "BorderSelected": "34, 100, 57",
+    "BorderDisabled": "87, 87, 87"
+  },
+  "PrimaryLightColor": {
+    "Normal": "160, 222, 182",
+    "Hover": "205, 237, 217",
+    "Pressed": "120, 166, 136",
+    "Selected": "152, 210, 172",
+    "Disabled": "205, 205, 205",
+    "DisabledSelected": "194, 194, 194",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "120, 166, 136",
+    "BorderHover": "124, 209, 154",
+    "BorderSelected": "114, 157, 129",
+    "BorderDisabled": "154, 154, 154"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "30, 86, 49",
+    "Hover": "86, 128, 100",
+    "Pressed": "22, 64, 36",
+    "Selected": "28, 81, 46",
+    "Disabled": "71, 71, 71",
+    "DisabledSelected": "67, 67, 67",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "22, 64, 36",
+    "BorderHover": "30, 86, 49",
+    "BorderSelected": "20, 60, 34",
+    "BorderDisabled": "53, 53, 53"
+  },
+  "SecondaryColor": {
+    "Normal": "122, 97, 65",
+    "Hover": "155, 136, 112",
+    "Pressed": "91, 72, 48",
+    "Selected": "115, 92, 61",
+    "Disabled": "100, 100, 100",
+    "DisabledSelected": "94, 94, 94",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "80, 63, 41",
+    "BorderHover": "90, 71, 48",
+    "BorderSelected": "86, 68, 45",
+    "BorderDisabled": "74, 74, 74"
+  },
+  "SecondaryLightColor": {
+    "Normal": "163, 135, 117",
+    "Hover": "186, 165, 151",
+    "Pressed": "122, 101, 87",
+    "Selected": "154, 128, 111",
+    "Disabled": "139, 139, 139",
+    "DisabledSelected": "132, 132, 132",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "122, 101, 87",
+    "BorderHover": "163, 135, 117",
+    "BorderSelected": "115, 95, 82",
+    "BorderDisabled": "104, 104, 104"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "95, 61, 44",
+    "Hover": "135, 109, 96",
+    "Pressed": "71, 45, 33",
+    "Selected": "90, 57, 41",
+    "Disabled": "67, 67, 67",
+    "DisabledSelected": "62, 62, 62",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "71, 45, 33",
+    "BorderHover": "95, 61, 44",
+    "BorderSelected": "67, 42, 31",
+    "BorderDisabled": "49, 49, 49"
+  },
+  "Surface": {
+    "Normal": "240, 255, 242",
+    "Hover": "191, 255, 210",
+    "Pressed": "180, 191, 181",
+    "Selected": "228, 242, 229",
+    "Disabled": "221, 221, 221",
+    "DisabledSelected": "238, 238, 238",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "180, 191, 181",
+    "BorderHover": "190, 243, 197",
+    "BorderSelected": "171, 181, 171",
+    "BorderDisabled": "187, 187, 187"
+  },
+  "Background": {
+    "Normal": "234, 255, 237",
+    "Hover": "239, 255, 241",
+    "Pressed": "175, 191, 177",
+    "Selected": "222, 242, 225",
+    "Disabled": "207, 207, 207",
+    "DisabledSelected": "236, 236, 236",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "175, 191, 177",
+    "BorderHover": "234, 255, 237",
+    "BorderSelected": "166, 181, 168",
+    "BorderDisabled": "186, 186, 186"
+  },
+  "PrimaryWidget": {
+    "Normal": "49, 142, 82",
+    "Hover": "100, 170, 125",
+    "Pressed": "36, 106, 61",
+    "Selected": "46, 134, 77",
+    "Disabled": "117, 117, 117",
+    "DisabledSelected": "111, 111, 111",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "36, 106, 61",
+    "BorderHover": "49, 142, 82",
+    "BorderSelected": "34, 100, 57",
+    "BorderDisabled": "87, 87, 87"
+  },
+  "SecondaryWidget": {
+    "Normal": "95, 61, 44",
+    "Hover": "135, 109, 96",
+    "Pressed": "71, 45, 33",
+    "Selected": "90, 57, 41",
+    "Disabled": "67, 67, 67",
+    "DisabledSelected": "62, 62, 62",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "71, 45, 33",
+    "BorderHover": "95, 61, 44",
+    "BorderSelected": "67, 42, 31",
+    "BorderDisabled": "49, 49, 49"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Morning Rays.skin b/editor source/SPNATI Character Editor/Resources/Skins/Morning Rays.skin
new file mode 100644
index 0000000000000000000000000000000000000000..035f856b99b49fd98098d8d8662b2134a0cfd78a
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Morning Rays.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "207, 200, 175",
+  "FieldBackColor": "White",
+  "FieldAltBackColor": "255, 244, 236",
+  "FieldDisabledBackColor": "ControlLightLight",
+  "LabelForeColor": "127, 0, 0, 0",
+  "PrimaryForeColor": "100, 28, 69",
+  "PrimaryLightForeColor": "White",
+  "SecondaryForeColor": "43, 89, 69",
+  "SecondaryLightForeColor": "200, 225, 205",
+  "Separator": "176, 89, 60",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "170, 0, 0",
+  "GoodForeColor": "Green",
+  "BadForeColor": "Red",
+  "FocusRectangle": "Black",
+  "Gray": "Gray",
+  "LightGray": "LightGray",
+  "Orange": "243, 99, 12",
+  "Green": "Green",
+  "Blue": "Blue",
+  "Purple": "Purple",
+  "Pink": "234, 85, 164",
+  "Red": "Red",
+  "Group1": "148, 89, 160",
+  "Group2": "0, 98, 173",
+  "Group3": "86, 119, 41",
+  "Group4": "175, 89, 49",
+  "Group5": "Black",
+  "AppColors": {
+    "TimelineRow": "236, 231, 213",
+    "TimelineRowAlt": "244, 241, 232",
+    "TimelineFore": "Black",
+    "TimelineSelected": "232, 207, 191",
+    "TimelineRowBorder": "150, 150, 150",
+    "TimelineSubRowBorder": "170, 170, 170",
+    "TimelineBar": "Red",
+    "PlaybackBar": "Green",
+    "WidgetTitleSelected": "White",
+    "WidgetTitle": "219, 209, 174",
+    "WidgetBorder": "Black",
+    "WidgetHeaderRow": "139, 194, 147",
+    "WidgetRow": "216, 231, 209",
+    "WidgetRowSelected": "219, 241, 218",
+    "WidgetRepeat": "103, 106, 116",
+    "KeyframeHeader": "255, 226, 66",
+    "Keyframe0": "180, 180, 180",
+    "KeyframeSelected": "245, 245, 255",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Morning Rays",
+  "Group": "Light",
+  "Description": "Vibes of a lazy spring morning",
+  "PrimaryColor": {
+    "Normal": "147, 83, 83",
+    "Hover": "174, 126, 126",
+    "Pressed": "110, 62, 62",
+    "Selected": "139, 78, 78",
+    "Disabled": "96, 96, 96",
+    "DisabledSelected": "90, 90, 90",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "110, 62, 62",
+    "BorderHover": "147, 83, 83",
+    "BorderSelected": "104, 58, 58",
+    "BorderDisabled": "72, 72, 72"
+  },
+  "PrimaryLightColor": {
+    "Normal": "208, 143, 100",
+    "Hover": "152, 95, 78",
+    "Pressed": "156, 107, 75",
+    "Selected": "197, 135, 95",
+    "Disabled": "153, 153, 153",
+    "DisabledSelected": "145, 145, 145",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "138, 95, 66",
+    "BorderHover": "112, 73, 63",
+    "BorderSelected": "148, 101, 71",
+    "BorderDisabled": "115, 115, 115"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "100, 28, 69",
+    "Hover": "138, 84, 115",
+    "Pressed": "75, 21, 51",
+    "Selected": "95, 26, 65",
+    "Disabled": "46, 46, 46",
+    "DisabledSelected": "43, 43, 43",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "75, 21, 51",
+    "BorderHover": "100, 28, 69",
+    "BorderSelected": "71, 19, 48",
+    "BorderDisabled": "34, 34, 34"
+  },
+  "SecondaryColor": {
+    "Normal": "71, 139, 124",
+    "Hover": "117, 168, 156",
+    "Pressed": "53, 104, 93",
+    "Selected": "67, 132, 117",
+    "Disabled": "123, 123, 123",
+    "DisabledSelected": "117, 117, 117",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "53, 104, 93",
+    "BorderHover": "71, 139, 124",
+    "BorderSelected": "50, 98, 88",
+    "BorderDisabled": "92, 92, 92"
+  },
+  "SecondaryLightColor": {
+    "Normal": "200, 225, 205",
+    "Hover": "160, 203, 169",
+    "Pressed": "150, 168, 153",
+    "Selected": "190, 213, 194",
+    "Disabled": "218, 218, 218",
+    "DisabledSelected": "206, 206, 206",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "150, 168, 153",
+    "BorderHover": "107, 173, 122",
+    "BorderSelected": "142, 159, 145",
+    "BorderDisabled": "163, 163, 163"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "43, 89, 69",
+    "Hover": "96, 130, 115",
+    "Pressed": "32, 66, 51",
+    "Selected": "40, 84, 65",
+    "Disabled": "77, 77, 77",
+    "DisabledSelected": "73, 73, 73",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "32, 66, 51",
+    "BorderHover": "43, 89, 69",
+    "BorderSelected": "30, 62, 48",
+    "BorderDisabled": "57, 57, 57"
+  },
+  "Surface": {
+    "Normal": "253, 246, 227",
+    "Hover": "255, 231, 204",
+    "Pressed": "189, 184, 170",
+    "Selected": "240, 233, 215",
+    "Disabled": "209, 209, 209",
+    "DisabledSelected": "233, 233, 233",
+    "ForeColor": "37, 56, 38",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "189, 184, 170",
+    "BorderHover": "210, 197, 170",
+    "BorderSelected": "179, 174, 161",
+    "BorderDisabled": "184, 184, 184"
+  },
+  "Background": {
+    "Normal": "236, 231, 213",
+    "Hover": "215, 191, 147",
+    "Pressed": "177, 173, 159",
+    "Selected": "224, 219, 202",
+    "Disabled": "185, 185, 185",
+    "DisabledSelected": "218, 218, 218",
+    "ForeColor": "54, 72, 53",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "177, 173, 159",
+    "BorderHover": "236, 231, 213",
+    "BorderSelected": "168, 164, 151",
+    "BorderDisabled": "172, 172, 172"
+  },
+  "PrimaryWidget": {
+    "Normal": "147, 83, 83",
+    "Hover": "174, 126, 126",
+    "Pressed": "110, 62, 62",
+    "Selected": "139, 78, 78",
+    "Disabled": "96, 96, 96",
+    "DisabledSelected": "90, 90, 90",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "110, 62, 62",
+    "BorderHover": "147, 83, 83",
+    "BorderSelected": "104, 58, 58",
+    "BorderDisabled": "72, 72, 72"
+  },
+  "SecondaryWidget": {
+    "Normal": "73, 139, 121",
+    "Hover": "118, 168, 154",
+    "Pressed": "54, 104, 90",
+    "Selected": "69, 132, 114",
+    "Disabled": "123, 123, 123",
+    "DisabledSelected": "117, 117, 117",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "54, 104, 90",
+    "BorderHover": "73, 139, 121",
+    "BorderSelected": "51, 98, 85",
+    "BorderDisabled": "92, 92, 92"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/Purple.skin b/editor source/SPNATI Character Editor/Resources/Skins/Purple.skin
new file mode 100644
index 0000000000000000000000000000000000000000..d48f4aeb6bfe4cff9b0f1cb240553185d726b65e
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/Purple.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "227, 207, 231",
+  "FieldBackColor": "White",
+  "FieldAltBackColor": "249, 242, 255",
+  "FieldDisabledBackColor": "235, 235, 235",
+  "LabelForeColor": "127, 0, 0, 0",
+  "PrimaryForeColor": "56, 0, 107",
+  "PrimaryLightForeColor": "213, 177, 233",
+  "SecondaryForeColor": "111, 121, 168",
+  "SecondaryLightForeColor": "209, 217, 255",
+  "Separator": "231, 210, 255",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "217, 0, 0",
+  "GoodForeColor": "Green",
+  "BadForeColor": "Red",
+  "FocusRectangle": "Black",
+  "Gray": "Gray",
+  "LightGray": "LightGray",
+  "Orange": "243, 99, 12",
+  "Green": "Green",
+  "Blue": "Blue",
+  "Purple": "Purple",
+  "Pink": "234, 85, 164",
+  "Red": "Red",
+  "Group1": "148, 89, 160",
+  "Group2": "0, 98, 173",
+  "Group3": "86, 119, 41",
+  "Group4": "175, 89, 49",
+  "Group5": "Black",
+  "AppColors": {
+    "TimelineRow": "251, 242, 255",
+    "TimelineRowAlt": "White",
+    "TimelineFore": "Black",
+    "TimelineSelected": "243, 230, 255",
+    "TimelineRowBorder": "143, 128, 172",
+    "TimelineSubRowBorder": "169, 159, 181",
+    "TimelineBar": "Red",
+    "PlaybackBar": "Green",
+    "WidgetTitleSelected": "White",
+    "WidgetTitle": "192, 177, 216",
+    "WidgetBorder": "Black",
+    "WidgetHeaderRow": "189, 143, 250",
+    "WidgetRow": "211, 203, 226",
+    "WidgetRowSelected": "226, 223, 236",
+    "WidgetRepeat": "103, 106, 116",
+    "KeyframeHeader": "255, 226, 66",
+    "Keyframe0": "180, 180, 180",
+    "KeyframeSelected": "245, 245, 255",
+    "KeyframeBorder": "Black"
+  },
+  "Name": "Purple",
+  "Group": "Light",
+  "Description": "Modern, simple purple",
+  "PrimaryColor": {
+    "Normal": "106, 27, 154",
+    "Hover": "143, 84, 179",
+    "Pressed": "79, 20, 115",
+    "Selected": "100, 25, 146",
+    "Disabled": "52, 52, 52",
+    "DisabledSelected": "49, 49, 49",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "79, 20, 115",
+    "BorderHover": "106, 27, 154",
+    "BorderSelected": "75, 19, 109",
+    "BorderDisabled": "39, 39, 39"
+  },
+  "PrimaryLightColor": {
+    "Normal": "156, 77, 204",
+    "Hover": "180, 121, 216",
+    "Pressed": "117, 57, 153",
+    "Selected": "148, 73, 193",
+    "Disabled": "102, 102, 102",
+    "DisabledSelected": "97, 97, 97",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "117, 57, 153",
+    "BorderHover": "156, 77, 204",
+    "BorderSelected": "111, 54, 145",
+    "BorderDisabled": "76, 76, 76"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "56, 0, 107",
+    "Hover": "105, 63, 144",
+    "Pressed": "42, 0, 80",
+    "Selected": "53, 0, 101",
+    "Disabled": "19, 19, 19",
+    "DisabledSelected": "18, 18, 18",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "42, 0, 80",
+    "BorderHover": "56, 0, 107",
+    "BorderSelected": "39, 0, 76",
+    "BorderDisabled": "14, 14, 14"
+  },
+  "SecondaryColor": {
+    "Normal": "159, 168, 218",
+    "Hover": "183, 189, 227",
+    "Pressed": "119, 126, 163",
+    "Selected": "151, 159, 207",
+    "Disabled": "DarkGray",
+    "DisabledSelected": "160, 160, 160",
+    "ForeColor": "Black",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "119, 126, 163",
+    "BorderHover": "159, 168, 218",
+    "BorderSelected": "113, 119, 154",
+    "BorderDisabled": "127, 127, 127"
+  },
+  "SecondaryLightColor": {
+    "Normal": "209, 217, 255",
+    "Hover": "220, 226, 255",
+    "Pressed": "156, 162, 191",
+    "Selected": "198, 206, 242",
+    "Disabled": "218, 218, 218",
+    "DisabledSelected": "206, 206, 206",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "156, 162, 191",
+    "BorderHover": "209, 217, 255",
+    "BorderSelected": "148, 153, 181",
+    "BorderDisabled": "162, 162, 162"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "111, 121, 168",
+    "Hover": "147, 154, 189",
+    "Pressed": "83, 90, 126",
+    "Selected": "105, 114, 159",
+    "Disabled": "122, 122, 122",
+    "DisabledSelected": "115, 115, 115",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "83, 90, 126",
+    "BorderHover": "111, 121, 168",
+    "BorderSelected": "78, 85, 119",
+    "BorderDisabled": "91, 91, 91"
+  },
+  "Surface": {
+    "Normal": "White",
+    "Hover": "245, 235, 254",
+    "Pressed": "191, 191, 191",
+    "Selected": "242, 242, 242",
+    "Disabled": "193, 193, 193",
+    "DisabledSelected": "242, 242, 242",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "191, 191, 191",
+    "BorderHover": "190, 177, 216",
+    "BorderSelected": "181, 181, 181",
+    "BorderDisabled": "191, 191, 191"
+  },
+  "Background": {
+    "Normal": "251, 242, 255",
+    "Hover": "252, 245, 255",
+    "Pressed": "188, 181, 191",
+    "Selected": "238, 229, 242",
+    "Disabled": "207, 207, 207",
+    "DisabledSelected": "231, 231, 231",
+    "ForeColor": "Black",
+    "DisabledForeColor": "70, 70, 70",
+    "Border": "188, 181, 191",
+    "BorderHover": "232, 213, 247",
+    "BorderSelected": "178, 171, 181",
+    "BorderDisabled": "183, 183, 183"
+  },
+  "PrimaryWidget": {
+    "Normal": "106, 27, 154",
+    "Hover": "143, 84, 179",
+    "Pressed": "79, 20, 115",
+    "Selected": "100, 25, 146",
+    "Disabled": "52, 52, 52",
+    "DisabledSelected": "49, 49, 49",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "79, 20, 115",
+    "BorderHover": "106, 27, 154",
+    "BorderSelected": "75, 19, 109",
+    "BorderDisabled": "39, 39, 39"
+  },
+  "SecondaryWidget": {
+    "Normal": "159, 168, 218",
+    "Hover": "183, 189, 227",
+    "Pressed": "119, 126, 163",
+    "Selected": "151, 159, 207",
+    "Disabled": "DarkGray",
+    "DisabledSelected": "160, 160, 160",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "119, 126, 163",
+    "BorderHover": "159, 168, 218",
+    "BorderSelected": "113, 119, 154",
+    "BorderDisabled": "127, 127, 127"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/Skins/The Inventory.skin b/editor source/SPNATI Character Editor/Resources/Skins/The Inventory.skin
new file mode 100644
index 0000000000000000000000000000000000000000..4c7f5d23759d53509a1b2a71b9d927ee0f85450f
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/Skins/The Inventory.skin	
@@ -0,0 +1,195 @@
+{
+  "SurfaceShadowColor": "92, 41, 29",
+  "FieldBackColor": "82, 60, 46",
+  "FieldAltBackColor": "105, 78, 58",
+  "FieldDisabledBackColor": "137, 129, 124",
+  "LabelForeColor": "192, 181, 152",
+  "PrimaryForeColor": "204, 146, 74",
+  "PrimaryLightForeColor": "213, 165, 106",
+  "SecondaryForeColor": "80, 152, 69",
+  "SecondaryLightForeColor": "38, 48, 18",
+  "Separator": "170, 116, 66",
+  "HoverOverlay": "50, 127, 127, 127",
+  "PressOverlay": "50, 64, 64, 64",
+  "ErrorBackColor": "170, 0, 0",
+  "GoodForeColor": "Green",
+  "BadForeColor": "Red",
+  "FocusRectangle": "249, 234, 172",
+  "Gray": "131, 131, 131",
+  "LightGray": "92, 92, 92",
+  "Orange": "255, 186, 91",
+  "Green": "0, 217, 0",
+  "Blue": "129, 179, 254",
+  "Purple": "214, 139, 245",
+  "Pink": "253, 193, 227",
+  "Red": "242, 87, 91",
+  "Group1": "232, 175, 235",
+  "Group2": "102, 190, 255",
+  "Group3": "175, 210, 128",
+  "Group4": "234, 170, 132",
+  "Group5": "White",
+  "AppColors": {
+    "TimelineRow": "68, 36, 24",
+    "TimelineRowAlt": "86, 47, 27",
+    "TimelineFore": "201, 143, 67",
+    "TimelineSelected": "92, 33, 26",
+    "TimelineRowBorder": "152, 99, 39",
+    "TimelineSubRowBorder": "115, 54, 32",
+    "TimelineBar": "251, 67, 47",
+    "PlaybackBar": "126, 143, 5",
+    "WidgetTitleSelected": "130, 46, 36",
+    "WidgetTitle": "68, 22, 17",
+    "WidgetBorder": "168, 94, 45",
+    "WidgetHeaderRow": "39, 49, 19",
+    "WidgetRow": "107, 56, 41",
+    "WidgetRowSelected": "84, 108, 51",
+    "WidgetRepeat": "203, 149, 75",
+    "KeyframeHeader": "216, 174, 118",
+    "Keyframe0": "180, 168, 146",
+    "KeyframeSelected": "224, 218, 180",
+    "KeyframeBorder": "65, 27, 27"
+  },
+  "Name": "The Inventory",
+  "Group": "Dark",
+  "Description": "Colors of a night at the Inventory",
+  "PrimaryColor": {
+    "Normal": "91, 32, 25",
+    "Hover": "132, 87, 82",
+    "Pressed": "68, 24, 18",
+    "Selected": "86, 30, 23",
+    "Disabled": "44, 44, 44",
+    "DisabledSelected": "41, 41, 41",
+    "ForeColor": "207, 157, 90",
+    "DisabledForeColor": "193, 193, 193",
+    "Border": "68, 24, 18",
+    "BorderHover": "91, 32, 25",
+    "BorderSelected": "64, 22, 17",
+    "BorderDisabled": "32, 32, 32"
+  },
+  "PrimaryLightColor": {
+    "Normal": "102, 55, 39",
+    "Hover": "140, 105, 93",
+    "Pressed": "76, 41, 29",
+    "Selected": "96, 52, 37",
+    "Disabled": "63, 63, 63",
+    "DisabledSelected": "60, 60, 60",
+    "ForeColor": "234, 209, 159",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "76, 41, 29",
+    "BorderHover": "102, 55, 39",
+    "BorderSelected": "72, 38, 27",
+    "BorderDisabled": "47, 47, 47"
+  },
+  "PrimaryDarkColor": {
+    "Normal": "74, 22, 21",
+    "Hover": "119, 80, 79",
+    "Pressed": "55, 16, 15",
+    "Selected": "70, 20, 19",
+    "Disabled": "32, 32, 32",
+    "DisabledSelected": "30, 30, 30",
+    "ForeColor": "216, 175, 120",
+    "DisabledForeColor": "200, 200, 200",
+    "Border": "55, 16, 15",
+    "BorderHover": "74, 22, 21",
+    "BorderSelected": "52, 15, 14",
+    "BorderDisabled": "24, 24, 24"
+  },
+  "SecondaryColor": {
+    "Normal": "39, 49, 19",
+    "Hover": "93, 100, 78",
+    "Pressed": "29, 36, 14",
+    "Selected": "37, 46, 18",
+    "Disabled": "44, 44, 44",
+    "DisabledSelected": "42, 42, 42",
+    "ForeColor": "219, 181, 130",
+    "DisabledForeColor": "196, 196, 196",
+    "Border": "29, 36, 14",
+    "BorderHover": "39, 49, 19",
+    "BorderSelected": "27, 34, 13",
+    "BorderDisabled": "32, 32, 32"
+  },
+  "SecondaryLightColor": {
+    "Normal": "80, 100, 38",
+    "Hover": "123, 138, 92",
+    "Pressed": "60, 75, 28",
+    "Selected": "76, 95, 36",
+    "Disabled": "91, 91, 91",
+    "DisabledSelected": "86, 86, 86",
+    "ForeColor": "255, 249, 223",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "37, 47, 17",
+    "BorderHover": "80, 100, 38",
+    "BorderSelected": "57, 71, 26",
+    "BorderDisabled": "68, 68, 68"
+  },
+  "SecondaryDarkColor": {
+    "Normal": "22, 32, 12",
+    "Hover": "80, 87, 72",
+    "Pressed": "16, 24, 9",
+    "Selected": "20, 30, 11",
+    "Disabled": "28, 28, 28",
+    "DisabledSelected": "26, 26, 26",
+    "ForeColor": "207, 157, 90",
+    "DisabledForeColor": "Silver",
+    "Border": "16, 24, 9",
+    "BorderHover": "22, 32, 12",
+    "BorderSelected": "15, 22, 8",
+    "BorderDisabled": "21, 21, 21"
+  },
+  "Surface": {
+    "Normal": "104, 56, 40",
+    "Hover": "141, 105, 93",
+    "Pressed": "78, 42, 30",
+    "Selected": "98, 53, 38",
+    "Disabled": "75, 65, 54",
+    "DisabledSelected": "61, 61, 61",
+    "ForeColor": "225, 205, 162",
+    "DisabledForeColor": "195, 195, 195",
+    "Border": "125, 81, 33",
+    "BorderHover": "182, 123, 52",
+    "BorderSelected": "74, 39, 28",
+    "BorderDisabled": "48, 48, 48"
+  },
+  "Background": {
+    "Normal": "68, 38, 24",
+    "Hover": "114, 92, 81",
+    "Pressed": "51, 28, 18",
+    "Selected": "64, 36, 22",
+    "Disabled": "69, 44, 39",
+    "DisabledSelected": "40, 40, 40",
+    "ForeColor": "223, 187, 134",
+    "DisabledForeColor": "196, 196, 196",
+    "Border": "129, 72, 35",
+    "BorderHover": "200, 137, 60",
+    "BorderSelected": "48, 26, 17",
+    "BorderDisabled": "32, 32, 32"
+  },
+  "PrimaryWidget": {
+    "Normal": "201, 137, 58",
+    "Hover": "214, 166, 107",
+    "Pressed": "150, 102, 43",
+    "Selected": "190, 130, 55",
+    "Disabled": "144, 144, 144",
+    "DisabledSelected": "137, 137, 137",
+    "ForeColor": "15, 7, 0",
+    "DisabledForeColor": "193, 193, 193",
+    "Border": "150, 102, 43",
+    "BorderHover": "201, 137, 58",
+    "BorderSelected": "142, 96, 40",
+    "BorderDisabled": "107, 107, 107"
+  },
+  "SecondaryWidget": {
+    "Normal": "69, 97, 35",
+    "Hover": "115, 136, 90",
+    "Pressed": "51, 72, 26",
+    "Selected": "78, 109, 39",
+    "Disabled": "86, 86, 86",
+    "DisabledSelected": "82, 82, 82",
+    "ForeColor": "White",
+    "DisabledForeColor": "180, 180, 180",
+    "Border": "51, 72, 26",
+    "BorderHover": "69, 97, 35",
+    "BorderSelected": "48, 68, 24",
+    "BorderDisabled": "64, 64, 64"
+  }
+}
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/Resources/license.txt b/editor source/SPNATI Character Editor/Resources/license.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5e2fa58a7014d46b1bec30e421b18588ace9610e
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/license.txt	
@@ -0,0 +1,9 @@
+The MIT License (MIT)
+
+Copyright (c) 2007 James Newton-King
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/editor source/SPNATI Character Editor/Resources/preview.html b/editor source/SPNATI Character Editor/Resources/preview.html
new file mode 100644
index 0000000000000000000000000000000000000000..16d11d5db5af43500f096ddab033c04b2f1a7d2b
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Resources/preview.html	
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <meta http-equiv='X-UA-Compatible' content='IE=edge' />
+    <meta charset="utf-8" />
+    <title></title>
+    <style>
+        body, html {
+            margin: 0;
+            padding: 0;
+            width: 100%;
+            height: 100%;
+        }
+
+        body {
+            font-family: "Trebuchet MS", Helvetica, sans-serif;
+            font-size: 14px;
+            color: #050505;
+            background-color: #050505;
+            box-sizing: border-box;
+        }
+
+        .dialogue-bubble-area {
+            display: table;
+            width: 100%;
+            height: 100%;
+            text-align: center;
+            font-size: 120%;
+            background: #F5F5F5;
+        }
+
+        .dialogue-bubble {
+            display: table-cell;
+            width: 90%;
+            height: 90%;
+            margin: 1%;
+            vertical-align: middle;
+        }
+    </style>
+</head>
+<body>
+    <div class="dialogue-bubble-area">
+        <div class="dialogue-bubble">
+            This is the <span id="preview">Preview Text</span> for the style.
+        </div>
+    </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/SPNATI Character Editor.csproj b/editor source/SPNATI Character Editor/SPNATI Character Editor.csproj
index d606486092776d248db9ce5dbc7033cbde017ab0..258191f1b94c96668cc0881406d71070fd5337f5 100644
--- a/editor source/SPNATI Character Editor/SPNATI Character Editor.csproj	
+++ b/editor source/SPNATI Character Editor/SPNATI Character Editor.csproj	
@@ -52,12 +52,17 @@
     <ApplicationIcon>editor.ico</ApplicationIcon>
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>.\Newtonsoft.Json.dll</HintPath>
+    </Reference>
     <Reference Include="NHunspell, Version=1.2.5554.16953, Culture=neutral, PublicKeyToken=1ac793ea843b4366, processorArchitecture=MSIL">
       <HintPath>..\packages\NHunspell.1.2.5554.16953\lib\net\NHunspell.dll</HintPath>
       <Private>True</Private>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
+    <Reference Include="System.IO.Compression.FileSystem" />
     <Reference Include="System.Windows.Forms.DataVisualization" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
@@ -77,11 +82,10 @@
     <Compile Include="Actions\DeleteWidgetCommand.cs" />
     <Compile Include="Actions\MoveSpriteStartCommand.cs" />
     <Compile Include="Actions\PasteKeyframeCommand.cs" />
-    <Compile Include="Actions\ToggleAnimationBreakCommand.cs" />
+    <Compile Include="Actions\TimelineActions\ModifyWidgetLengthTimelineAction.cs" />
     <Compile Include="Actions\TimelineActions\MoveWidgetTimelineAction.cs" />
     <Compile Include="Actions\TimelineActions\SelectKeyframeTimelineAction.cs" />
-    <Compile Include="Actions\TimelineActions\WidgetEndTimelineAction.cs" />
-    <Compile Include="Actions\TimelineActions\WidgetStartTimelineAction.cs" />
+    <Compile Include="Actions\ToggleKeyframeTypeCommand.cs" />
     <Compile Include="Activities\AdvancedMetadataEditor.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -94,6 +98,18 @@
     <Compile Include="Activities\BanterWizard.Designer.cs">
       <DependentUpon>BanterWizard.cs</DependentUpon>
     </Compile>
+    <Compile Include="Activities\CaseDataSlicer.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Activities\CaseDataSlicer.Designer.cs">
+      <DependentUpon>CaseDataSlicer.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Activities\CharacterConfiguration.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Activities\CharacterConfiguration.Designer.cs">
+      <DependentUpon>CharacterConfiguration.cs</DependentUpon>
+    </Compile>
     <Compile Include="Activities\CharacterPreview.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -136,6 +152,30 @@
     <Compile Include="Activities\DictionaryEditor.Designer.cs">
       <DependentUpon>DictionaryEditor.cs</DependentUpon>
     </Compile>
+    <Compile Include="Activities\LineImporter.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Activities\LineImporter.Designer.cs">
+      <DependentUpon>LineImporter.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Activities\PoseUsageGraph.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Activities\PoseUsageGraph.Designer.cs">
+      <DependentUpon>PoseUsageGraph.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Activities\RecipeEditor.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Activities\RecipeEditor.Designer.cs">
+      <DependentUpon>RecipeEditor.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Activities\RespondToThis.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Activities\RespondToThis.Designer.cs">
+      <DependentUpon>RespondToThis.cs</DependentUpon>
+    </Compile>
     <Compile Include="Activities\ScreenshotTaker.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -160,10 +200,17 @@
     <Compile Include="Activities\SpellCheck.Designer.cs">
       <DependentUpon>SpellCheck.cs</DependentUpon>
     </Compile>
+    <Compile Include="Activities\ThemeEditor.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Activities\ThemeEditor.Designer.cs">
+      <DependentUpon>ThemeEditor.cs</DependentUpon>
+    </Compile>
     <Compile Include="Analyzers\AnalyzerReportCriteria.cs" />
     <Compile Include="Analyzers\CollectibleAnalyzers.cs" />
     <Compile Include="Analyzers\DataCriterion.cs" />
     <Compile Include="Analyzers\DialogueAnalyzers.cs" />
+    <Compile Include="Analyzers\StatusAnalyzer.cs" />
     <Compile Include="Analyzers\VersionAnalyzer.cs" />
     <Compile Include="Analyzers\WardrobeAnalyzers.cs" />
     <Compile Include="Analyzers\MetadataAnalyzers.cs" />
@@ -175,9 +222,15 @@
     <Compile Include="Analyzers\TagAnalyzer.cs" />
     <Compile Include="Analyzers\PoseDirectiveAnalyzers.cs" />
     <Compile Include="Analyzers\PoseCountAnalyzer.cs" />
+    <Compile Include="Categories\RelativePosition.cs" />
+    <Compile Include="Categories\Slot.cs" />
     <Compile Include="Charts\Builders\CollectibleBuilder.cs" />
+    <Compile Include="Charts\Builders\ClothingBuilder.cs" />
     <Compile Include="Charts\Builders\CustomPoseBuilder.cs" />
     <Compile Include="Charts\Builders\StorageSpaceImagesBuilder.cs" />
+    <Compile Include="Charts\SkinnedChart.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
     <Compile Include="Controls\CaseControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -202,52 +255,55 @@
     <Compile Include="Controls\EditControls\AnimDurationControl.Designer.cs">
       <DependentUpon>AnimDurationControl.cs</DependentUpon>
     </Compile>
-    <Compile Include="Controls\EditControls\CharacterTagControl.cs">
+    <Compile Include="Controls\EditControls\LayerDifferenceControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
-    <Compile Include="Controls\EditControls\CharacterTagControl.Designer.cs">
-      <DependentUpon>CharacterTagControl.cs</DependentUpon>
+    <Compile Include="Controls\EditControls\LayerDifferenceControl.Designer.cs">
+      <DependentUpon>LayerDifferenceControl.cs</DependentUpon>
     </Compile>
-    <Compile Include="Controls\EditControls\CharacterCollectibleControl.cs">
+    <Compile Include="Controls\EditControls\OneShotControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
-    <Compile Include="Controls\EditControls\CharacterCollectibleControl.Designer.cs">
-      <DependentUpon>CharacterCollectibleControl.cs</DependentUpon>
+    <Compile Include="Controls\EditControls\OneShotControl.Designer.cs">
+      <DependentUpon>OneShotControl.cs</DependentUpon>
     </Compile>
-    <Compile Include="Controls\EditControls\CharacterCollectibleCountControl.cs">
+    <Compile Include="Controls\EditControls\VariableControls\PlayerStageControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
-    <Compile Include="Controls\EditControls\CharacterCollectibleCountControl.Designer.cs">
-      <DependentUpon>CharacterCollectibleCountControl.cs</DependentUpon>
+    <Compile Include="Controls\EditControls\VariableControls\PlayerStageControl.Designer.cs">
+      <DependentUpon>PlayerStageControl.cs</DependentUpon>
     </Compile>
-    <Compile Include="Controls\EditControls\CharacterPersistentMarkerControl.cs">
+    <Compile Include="Controls\EditControls\VariableControls\CategoryControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
-    <Compile Include="Controls\EditControls\CharacterPersistentMarkerControl.Designer.cs">
-      <DependentUpon>CharacterPersistentMarkerControl.cs</DependentUpon>
+    <Compile Include="Controls\EditControls\VariableControls\CategoryControl.Designer.cs">
+      <DependentUpon>CategoryControl.cs</DependentUpon>
     </Compile>
-    <Compile Include="Controls\EditControls\PersistentMarkerControl.cs">
+    <Compile Include="Controls\EditControls\VariableControls\PersistentMarkerControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
-    <Compile Include="Controls\EditControls\PersistentMarkerControl.Designer.cs">
+    <Compile Include="Controls\EditControls\VariableControls\PersistentMarkerControl.Designer.cs">
       <DependentUpon>PersistentMarkerControl.cs</DependentUpon>
     </Compile>
-    <Compile Include="Controls\EditControls\TagControl.cs">
+    <Compile Include="Controls\EditControls\PlacementControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\EditControls\VariableControls\TagControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
-    <Compile Include="Controls\EditControls\TagControl.Designer.cs">
+    <Compile Include="Controls\EditControls\VariableControls\TagControl.Designer.cs">
       <DependentUpon>TagControl.cs</DependentUpon>
     </Compile>
-    <Compile Include="Controls\EditControls\CollectibleCountControl.cs">
+    <Compile Include="Controls\EditControls\VariableControls\CollectibleCountControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
-    <Compile Include="Controls\EditControls\CollectibleCountControl.Designer.cs">
+    <Compile Include="Controls\EditControls\VariableControls\CollectibleCountControl.Designer.cs">
       <DependentUpon>CollectibleCountControl.cs</DependentUpon>
     </Compile>
-    <Compile Include="Controls\EditControls\CollectibleControl.cs">
+    <Compile Include="Controls\EditControls\VariableControls\CollectibleControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
-    <Compile Include="Controls\EditControls\CollectibleControl.Designer.cs">
+    <Compile Include="Controls\EditControls\VariableControls\CollectibleControl.Designer.cs">
       <DependentUpon>CollectibleControl.cs</DependentUpon>
     </Compile>
     <Compile Include="Controls\EditControls\DirectiveMarkerControl.cs">
@@ -262,6 +318,18 @@
     <Compile Include="Controls\EditControls\ExpressionControl.Designer.cs">
       <DependentUpon>ExpressionControl.cs</DependentUpon>
     </Compile>
+    <Compile Include="Controls\EditControls\VariableControls\CostumeControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\EditControls\VariableControls\CostumeControl.Designer.cs">
+      <DependentUpon>CostumeControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\EditControls\VariableControls\PlayerControlBase.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\EditControls\VariableControls\PlayerControlBase.Designer.cs">
+      <DependentUpon>PlayerControlBase.cs</DependentUpon>
+    </Compile>
     <Compile Include="Controls\EditControls\VerticalAlignmentControl.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -301,6 +369,18 @@
     <Compile Include="Controls\CharacterImageDialog.cs">
       <SubType>Component</SubType>
     </Compile>
+    <Compile Include="Controls\IntervalSlider.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\IntervalSlider.Designer.cs">
+      <DependentUpon>IntervalSlider.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\LiveEpilogueEditor.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\LiveEpilogueEditor.Designer.cs">
+      <DependentUpon>LiveEpilogueEditor.cs</DependentUpon>
+    </Compile>
     <Compile Include="Controls\MarkerOptions.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -313,6 +393,12 @@
     <Compile Include="Controls\PartTransparencySlider.Designer.cs">
       <DependentUpon>PartTransparencySlider.cs</DependentUpon>
     </Compile>
+    <Compile Include="Controls\Reference\TagGuide.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\Reference\TagGuide.Designer.cs">
+      <DependentUpon>TagGuide.cs</DependentUpon>
+    </Compile>
     <Compile Include="Controls\SceneTree.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -325,12 +411,96 @@
     <Compile Include="Controls\RecordSelectBox.Designer.cs">
       <DependentUpon>RecordSelectBox.cs</DependentUpon>
     </Compile>
+    <Compile Include="Controls\SlicerControls\IntervalSlicerControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\SlicerControls\IntervalSlicerControl.Designer.cs">
+      <DependentUpon>IntervalSlicerControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\SlicerControls\OneShotSlicerControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\SlicerControls\OneShotSlicerControl.Designer.cs">
+      <DependentUpon>OneShotSlicerControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\SlicerControls\StageSlicerControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\SlicerControls\StageSlicerControl.Designer.cs">
+      <DependentUpon>StageSlicerControl.cs</DependentUpon>
+    </Compile>
     <Compile Include="Controls\StageSpecificGrid.cs">
       <SubType>UserControl</SubType>
     </Compile>
     <Compile Include="Controls\StageSpecificGrid.Designer.cs">
       <DependentUpon>StageSpecificGrid.cs</DependentUpon>
     </Compile>
+    <Compile Include="Controls\StageGrid.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\StageGrid.Designer.cs">
+      <DependentUpon>StageGrid.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\StyleControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\StyleControl.Designer.cs">
+      <DependentUpon>StyleControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleAttributeEditControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleAttributeEditControl.Designer.cs">
+      <DependentUpon>StyleAttributeEditControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleColorControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleColorControl.Designer.cs">
+      <DependentUpon>StyleColorControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleFontFamilyControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleFontFamilyControl.Designer.cs">
+      <DependentUpon>StyleFontFamilyControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleFontSizeControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleFontSizeControl.Designer.cs">
+      <DependentUpon>StyleFontSizeControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleFontStyleControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleFontStyleControl.Designer.cs">
+      <DependentUpon>StyleFontStyleControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleFontVariantControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleFontVariantControl.Designer.cs">
+      <DependentUpon>StyleFontVariantControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleFontWeightControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleFontWeightControl.Designer.cs">
+      <DependentUpon>StyleFontWeightControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleTextDecorationControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleTextDecorationControl.Designer.cs">
+      <DependentUpon>StyleTextDecorationControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleTextShadowControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\StyleControls\StyleTextShadowControl.Designer.cs">
+      <DependentUpon>StyleTextShadowControl.cs</DependentUpon>
+    </Compile>
     <Compile Include="Controls\TagGrid.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -346,9 +516,15 @@
     <Compile Include="Controls\TreeViews\IDialogueTreeView.cs" />
     <Compile Include="Controls\TreeViews\CaseView.cs" />
     <Compile Include="Controls\TreeViews\StageView.cs" />
+    <Compile Include="DataSlicers\IntervalSlicer.cs" />
+    <Compile Include="DataSlicers\OneShotSlicer.cs" />
+    <Compile Include="DataSlicers\StageSlicer.cs" />
     <Compile Include="DataStructures\BindableTag.cs" />
     <Compile Include="DataStructures\BindableTagList.cs" />
+    <Compile Include="DataStructures\CaseLabel.cs" />
     <Compile Include="DataStructures\CaseNote.cs" />
+    <Compile Include="DataStructures\Choice.cs" />
+    <Compile Include="DataStructures\ClothingDatabase.cs" />
     <Compile Include="DataStructures\Collectible.cs" />
     <Compile Include="DataStructures\CollectibleData.cs" />
     <Compile Include="DataStructures\DataConversions.cs" />
@@ -542,12 +718,6 @@
     <Compile Include="Controls\SelectBox.Designer.cs">
       <DependentUpon>SelectBox.cs</DependentUpon>
     </Compile>
-    <Compile Include="Controls\TagControl.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="Controls\TagControl.Designer.cs">
-      <DependentUpon>TagControl.cs</DependentUpon>
-    </Compile>
     <Compile Include="Activities\WardrobeEditor.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -580,27 +750,46 @@
     <Compile Include="DataStructures\CountedSet.cs" />
     <Compile Include="DataStructures\Directive.cs" />
     <Compile Include="DataStructures\Epilogue.cs" />
+    <Compile Include="DataStructures\ExpressionTest.cs" />
     <Compile Include="DataStructures\ISkin.cs" />
     <Compile Include="DataStructures\Marker.cs" />
     <Compile Include="DataStructures\MarkerData.cs" />
     <Compile Include="DataStructures\CharacterEditorData.cs" />
+    <Compile Include="DataStructures\Nickname.cs" />
     <Compile Include="DataStructures\Pose.cs" />
     <Compile Include="DataStructures\PoseDirective.cs" />
+    <Compile Include="DataStructures\Recipe.cs" />
     <Compile Include="DataStructures\Scene.cs" />
     <Compile Include="DataStructures\Screen.cs" />
     <Compile Include="DataStructures\Skin.cs" />
     <Compile Include="DataStructures\SpellChecker.cs" />
     <Compile Include="DataStructures\SpnatiConfig.cs" />
     <Compile Include="DataStructures\Sprite.cs" />
+    <Compile Include="DataStructures\CharacterStyleSheet.cs" />
     <Compile Include="DataStructures\Tag.cs" />
     <Compile Include="DataStructures\TagDictionary.cs" />
+    <Compile Include="DataStructures\TargetCondition.cs" />
+    <Compile Include="DataStructures\TargetId.cs" />
     <Compile Include="DataStructures\Variable.cs" />
     <Compile Include="DataStructures\VariableDatabase.cs" />
     <Compile Include="DesktopMessages.cs" />
     <Compile Include="DialogueDatabase.cs" />
     <Compile Include="DialogueLine.cs" />
-    <Compile Include="EpilogueEditing\DataStructures\AnimatedProperty.cs" />
     <Compile Include="EpilogueEditing\AnimationHelpers.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveAnimatedObject.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveBreak.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveBubble.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveBubbleKeyframe.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveCamera.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveCameraKeyframe.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveData.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveEmitter.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveEmitterKeyframe.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveKeyframeMetadata.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveObject.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveParticle.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveScene.cs" />
+    <Compile Include="EpilogueEditing\DataStructures\LiveSpriteKeyframe.cs" />
     <Compile Include="EpilogueEditing\DirectiveDefinition.cs" />
     <Compile Include="EpilogueEditing\EpilogueCanvas.cs">
       <SubType>UserControl</SubType>
@@ -608,9 +797,13 @@
     <Compile Include="EpilogueEditing\EpilogueCanvas.Designer.cs">
       <DependentUpon>EpilogueCanvas.cs</DependentUpon>
     </Compile>
+    <Compile Include="EpilogueEditing\Interfaces\ICanInvalidate.cs" />
+    <Compile Include="EpilogueEditing\Interfaces\ICanvasViewport.cs" />
     <Compile Include="EpilogueEditing\Interfaces\ILabel.cs" />
     <Compile Include="EpilogueEditing\Interfaces\ITimelineAction.cs" />
+    <Compile Include="EpilogueEditing\Interfaces\ITimelineBreak.cs" />
     <Compile Include="EpilogueEditing\Interfaces\ITimelineData.cs" />
+    <Compile Include="EpilogueEditing\Interfaces\ITimelineObject.cs" />
     <Compile Include="EpilogueEditing\Interfaces\ITimelineWidget.cs" />
     <Compile Include="EpilogueEditing\LiveCanvas.cs">
       <SubType>UserControl</SubType>
@@ -629,7 +822,6 @@
       <DependentUpon>PoseEditor.cs</DependentUpon>
     </Compile>
     <Compile Include="EpilogueEditing\PropertyDefinition.cs" />
-    <Compile Include="EpilogueEditing\SimpleIK.cs" />
     <Compile Include="EpilogueEditing\RandomParameter.cs" />
     <Compile Include="EpilogueEditing\SceneAnimation.cs" />
     <Compile Include="EpilogueEditing\SceneObject.cs" />
@@ -637,6 +829,9 @@
     <Compile Include="EpilogueEditing\SceneParticle.cs" />
     <Compile Include="EpilogueEditing\ScenePreview.cs" />
     <Compile Include="EpilogueEditing\SceneTransition.cs" />
+    <Compile Include="EpilogueEditing\Widgets\CameraWidget.cs" />
+    <Compile Include="EpilogueEditing\Widgets\EmitterWidget.cs" />
+    <Compile Include="EpilogueEditing\Widgets\KeyframedWidget.cs" />
     <Compile Include="EpilogueEditing\Widgets\SpriteWidget.cs" />
     <Compile Include="EpilogueEditing\Timeline.cs">
       <SubType>UserControl</SubType>
@@ -644,14 +839,9 @@
     <Compile Include="EpilogueEditing\Timeline.Designer.cs">
       <DependentUpon>Timeline.cs</DependentUpon>
     </Compile>
+    <Compile Include="EpilogueEditing\Widgets\TextWidget.cs" />
     <Compile Include="ErrorLog.cs" />
     <Compile Include="ExtensionMethods.cs" />
-    <Compile Include="FileNameSelect.cs">
-      <SubType>Form</SubType>
-    </Compile>
-    <Compile Include="FileNameSelect.Designer.cs">
-      <DependentUpon>FileNameSelect.cs</DependentUpon>
-    </Compile>
     <Compile Include="Forms\CanvasHelp.cs">
       <SubType>Form</SubType>
     </Compile>
@@ -670,11 +860,35 @@
     <Compile Include="Forms\CreateSequenceForm.Designer.cs">
       <DependentUpon>CreateSequenceForm.cs</DependentUpon>
     </Compile>
-    <Compile Include="Forms\DialogueResponder.cs">
+    <Compile Include="Forms\CropCopier.cs">
       <SubType>Form</SubType>
     </Compile>
-    <Compile Include="Forms\DialogueResponder.Designer.cs">
-      <DependentUpon>DialogueResponder.cs</DependentUpon>
+    <Compile Include="Forms\CropCopier.Designer.cs">
+      <DependentUpon>CropCopier.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Forms\DialogueLegend.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\DialogueLegend.Designer.cs">
+      <DependentUpon>DialogueLegend.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Forms\ErrorTrace.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\ErrorTrace.Designer.cs">
+      <DependentUpon>ErrorTrace.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Forms\FailedImport.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\FailedImport.Designer.cs">
+      <DependentUpon>FailedImport.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Forms\FirstLaunchSetup.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\FirstLaunchSetup.Designer.cs">
+      <DependentUpon>FirstLaunchSetup.cs</DependentUpon>
     </Compile>
     <Compile Include="Forms\GameConfig.cs">
       <SubType>Form</SubType>
@@ -712,12 +926,24 @@
     <Compile Include="Forms\MarkerSetup.Designer.cs">
       <DependentUpon>MarkerSetup.cs</DependentUpon>
     </Compile>
+    <Compile Include="Forms\PoseExporter.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\PoseExporter.Designer.cs">
+      <DependentUpon>PoseExporter.cs</DependentUpon>
+    </Compile>
     <Compile Include="Forms\PoseSettingsForm.cs">
       <SubType>Form</SubType>
     </Compile>
     <Compile Include="Forms\PoseSettingsForm.Designer.cs">
       <DependentUpon>PoseSettingsForm.cs</DependentUpon>
     </Compile>
+    <Compile Include="Forms\DiscardResponsePrompt.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\DiscardResponsePrompt.Designer.cs">
+      <DependentUpon>DiscardResponsePrompt.cs</DependentUpon>
+    </Compile>
     <Compile Include="Forms\ResponseSetupForm.cs">
       <SubType>Form</SubType>
     </Compile>
@@ -730,6 +956,12 @@
     <Compile Include="Forms\SituationLinker.Designer.cs">
       <DependentUpon>SituationLinker.cs</DependentUpon>
     </Compile>
+    <Compile Include="Forms\StageImageSelection.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\StageImageSelection.Designer.cs">
+      <DependentUpon>StageImageSelection.cs</DependentUpon>
+    </Compile>
     <Compile Include="Forms\StageSelect.cs">
       <SubType>Form</SubType>
     </Compile>
@@ -754,6 +986,7 @@
     <Compile Include="Forms\WhatsNew.Designer.cs">
       <DependentUpon>WhatsNew.cs</DependentUpon>
     </Compile>
+    <Compile Include="GifWriter.cs" />
     <Compile Include="GUIHelper.cs" />
     <Compile Include="HelpForm.cs">
       <SubType>Form</SubType>
@@ -767,6 +1000,7 @@
     <Compile Include="IO\FlatFileSerializer.cs" />
     <Compile Include="IO\SpnatiXmlSerializer.cs" />
     <Compile Include="DataStructures\Listing.cs" />
+    <Compile Include="IO\CharacterStyleSheetSerializer.cs" />
     <Compile Include="KisekaeConverter.cs" />
     <Compile Include="MathExtensions.cs" />
     <Compile Include="MathUtil.cs" />
@@ -780,9 +1014,13 @@
     <Compile Include="Providers\DefinitionProvider.cs" />
     <Compile Include="Providers\CollectibleProvider.cs" />
     <Compile Include="Providers\RoleProvider.cs" />
-    <Compile Include="Providers\SkinProvider.cs" />
+    <Compile Include="Providers\CostumeProvider.cs" />
     <Compile Include="Providers\MarkerProvider.cs" />
+    <Compile Include="Providers\StyleControlProvider.cs" />
     <Compile Include="Providers\TagProvider.cs" />
+    <Compile Include="Providers\TargetProvider.cs" />
+    <Compile Include="Serialization\ImportEdit.cs" />
+    <Compile Include="WorkflowFilter.cs" />
     <Compile Include="WorkspaceMessages.cs" />
     <Compile Include="NewCharacterPrompt.cs">
       <SubType>Form</SubType>
@@ -821,6 +1059,12 @@
     <EmbeddedResource Include="Activities\BanterWizard.resx">
       <DependentUpon>BanterWizard.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Activities\CaseDataSlicer.resx">
+      <DependentUpon>CaseDataSlicer.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Activities\CharacterConfiguration.resx">
+      <DependentUpon>CharacterConfiguration.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Activities\CharacterPreview.resx">
       <DependentUpon>CharacterPreview.cs</DependentUpon>
     </EmbeddedResource>
@@ -842,6 +1086,9 @@
     <EmbeddedResource Include="Activities\DictionaryEditor.resx">
       <DependentUpon>DictionaryEditor.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Activities\LineImporter.resx">
+      <DependentUpon>LineImporter.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Activities\MarkerEditor.resx">
       <DependentUpon>MarkerEditor.cs</DependentUpon>
     </EmbeddedResource>
@@ -854,6 +1101,15 @@
     <EmbeddedResource Include="Activities\Loader.resx">
       <DependentUpon>Loader.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Activities\PoseUsageGraph.resx">
+      <DependentUpon>PoseUsageGraph.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Activities\RecipeEditor.resx">
+      <DependentUpon>RecipeEditor.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Activities\RespondToThis.resx">
+      <DependentUpon>RespondToThis.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Activities\ScreenshotTaker.resx">
       <DependentUpon>ScreenshotTaker.cs</DependentUpon>
     </EmbeddedResource>
@@ -878,6 +1134,9 @@
     <EmbeddedResource Include="Activities\TemplateEditor.resx">
       <DependentUpon>TemplateEditor.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Activities\ThemeEditor.resx">
+      <DependentUpon>ThemeEditor.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Activities\ValidateActivity.resx">
       <DependentUpon>ValidateActivity.cs</DependentUpon>
     </EmbeddedResource>
@@ -914,28 +1173,31 @@
     <EmbeddedResource Include="Controls\DialogueTree.resx">
       <DependentUpon>DialogueTree.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="Controls\EditControls\CharacterTagControl.resx">
-      <DependentUpon>CharacterTagControl.cs</DependentUpon>
+    <EmbeddedResource Include="Controls\EditControls\LayerDifferenceControl.resx">
+      <DependentUpon>LayerDifferenceControl.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="Controls\EditControls\CharacterCollectibleControl.resx">
-      <DependentUpon>CharacterCollectibleControl.cs</DependentUpon>
+    <EmbeddedResource Include="Controls\EditControls\OneShotControl.resx">
+      <DependentUpon>OneShotControl.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="Controls\EditControls\CharacterCollectibleCountControl.resx">
-      <DependentUpon>CharacterCollectibleCountControl.cs</DependentUpon>
+    <EmbeddedResource Include="Controls\EditControls\VariableControls\PlayerStageControl.resx">
+      <DependentUpon>PlayerStageControl.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="Controls\EditControls\CharacterPersistentMarkerControl.resx">
-      <DependentUpon>CharacterPersistentMarkerControl.cs</DependentUpon>
+    <EmbeddedResource Include="Controls\EditControls\VariableControls\CategoryControl.resx">
+      <DependentUpon>CategoryControl.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="Controls\EditControls\PersistentMarkerControl.resx">
+    <EmbeddedResource Include="Controls\EditControls\VariableControls\PersistentMarkerControl.resx">
       <DependentUpon>PersistentMarkerControl.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="Controls\EditControls\TagControl.resx">
+    <EmbeddedResource Include="Controls\EditControls\PlacementControl.resx">
+      <DependentUpon>PlacementControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\EditControls\VariableControls\TagControl.resx">
       <DependentUpon>TagControl.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="Controls\EditControls\CollectibleCountControl.resx">
+    <EmbeddedResource Include="Controls\EditControls\VariableControls\CollectibleCountControl.resx">
       <DependentUpon>CollectibleCountControl.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="Controls\EditControls\CollectibleControl.resx">
+    <EmbeddedResource Include="Controls\EditControls\VariableControls\CollectibleControl.resx">
       <DependentUpon>CollectibleControl.cs</DependentUpon>
     </EmbeddedResource>
     <EmbeddedResource Include="Controls\EditControls\DirectiveMarkerControl.resx">
@@ -947,6 +1209,12 @@
     <EmbeddedResource Include="Controls\EditControls\FilterControl.resx">
       <DependentUpon>FilterControl.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Controls\EditControls\VariableControls\CostumeControl.resx">
+      <DependentUpon>CostumeControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\EditControls\VariableControls\PlayerControlBase.resx">
+      <DependentUpon>PlayerControlBase.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Controls\EditControls\VerticalAlignmentControl.resx">
       <DependentUpon>VerticalAlignmentControl.cs</DependentUpon>
     </EmbeddedResource>
@@ -986,6 +1254,9 @@
     <EmbeddedResource Include="Controls\IntellisenseControl.resx">
       <DependentUpon>IntellisenseControl.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Controls\IntervalSlider.resx">
+      <DependentUpon>IntervalSlider.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Controls\KeyboardDataGridView.resx">
       <DependentUpon>KeyboardDataGridView.cs</DependentUpon>
     </EmbeddedResource>
@@ -1004,24 +1275,66 @@
     <EmbeddedResource Include="Controls\PartTransparencySlider.resx">
       <DependentUpon>PartTransparencySlider.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Controls\Reference\TagGuide.resx">
+      <DependentUpon>TagGuide.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Controls\SceneTree.resx">
       <DependentUpon>SceneTree.cs</DependentUpon>
     </EmbeddedResource>
     <EmbeddedResource Include="Controls\RecordSelectBox.resx">
       <DependentUpon>RecordSelectBox.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Controls\SlicerControls\IntervalSlicerControl.resx">
+      <DependentUpon>IntervalSlicerControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\SlicerControls\OneShotSlicerControl.resx">
+      <DependentUpon>OneShotSlicerControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\SlicerControls\StageSlicerControl.resx">
+      <DependentUpon>StageSlicerControl.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Controls\StageSpecificGrid.resx">
       <DependentUpon>StageSpecificGrid.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="Controls\TagControl.resx">
-      <DependentUpon>TagControl.cs</DependentUpon>
-    </EmbeddedResource>
     <EmbeddedResource Include="Controls\SelectBox.resx">
       <DependentUpon>SelectBox.cs</DependentUpon>
     </EmbeddedResource>
     <EmbeddedResource Include="Activities\WardrobeEditor.resx">
       <DependentUpon>WardrobeEditor.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Controls\StageGrid.resx">
+      <DependentUpon>StageGrid.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\StyleControl.resx">
+      <DependentUpon>StyleControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\StyleControls\StyleAttributeEditControl.resx">
+      <DependentUpon>StyleAttributeEditControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\StyleControls\StyleColorControl.resx">
+      <DependentUpon>StyleColorControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\StyleControls\StyleFontFamilyControl.resx">
+      <DependentUpon>StyleFontFamilyControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\StyleControls\StyleFontSizeControl.resx">
+      <DependentUpon>StyleFontSizeControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\StyleControls\StyleFontStyleControl.resx">
+      <DependentUpon>StyleFontStyleControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\StyleControls\StyleFontVariantControl.resx">
+      <DependentUpon>StyleFontVariantControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\StyleControls\StyleFontWeightControl.resx">
+      <DependentUpon>StyleFontWeightControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\StyleControls\StyleTextDecorationControl.resx">
+      <DependentUpon>StyleTextDecorationControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\StyleControls\StyleTextShadowControl.resx">
+      <DependentUpon>StyleTextShadowControl.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Controls\TagGrid.resx">
       <DependentUpon>TagGrid.cs</DependentUpon>
     </EmbeddedResource>
@@ -1043,8 +1356,8 @@
     <EmbeddedResource Include="EpilogueEditing\Timeline.resx">
       <DependentUpon>Timeline.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="FileNameSelect.resx">
-      <DependentUpon>FileNameSelect.cs</DependentUpon>
+    <EmbeddedResource Include="Forms\CanvasHelp.resx">
+      <DependentUpon>CanvasHelp.cs</DependentUpon>
     </EmbeddedResource>
     <EmbeddedResource Include="Forms\ChangeLogReview.resx">
       <DependentUpon>ChangeLogReview.cs</DependentUpon>
@@ -1052,8 +1365,23 @@
     <EmbeddedResource Include="Forms\CreateSequenceForm.resx">
       <DependentUpon>CreateSequenceForm.cs</DependentUpon>
     </EmbeddedResource>
-    <EmbeddedResource Include="Forms\DialogueResponder.resx">
-      <DependentUpon>DialogueResponder.cs</DependentUpon>
+    <EmbeddedResource Include="Forms\CropCopier.resx">
+      <DependentUpon>CropCopier.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\DialogueLegend.resx">
+      <DependentUpon>DialogueLegend.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\DiscardResponsePrompt.resx">
+      <DependentUpon>DiscardResponsePrompt.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\ErrorTrace.resx">
+      <DependentUpon>ErrorTrace.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\FailedImport.resx">
+      <DependentUpon>FailedImport.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\FirstLaunchSetup.resx">
+      <DependentUpon>FirstLaunchSetup.cs</DependentUpon>
     </EmbeddedResource>
     <EmbeddedResource Include="Forms\GameConfig.resx">
       <DependentUpon>GameConfig.cs</DependentUpon>
@@ -1073,6 +1401,9 @@
     <EmbeddedResource Include="Forms\MarkerSetup.resx">
       <DependentUpon>MarkerSetup.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Forms\PoseExporter.resx">
+      <DependentUpon>PoseExporter.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Forms\PoseSettingsForm.resx">
       <DependentUpon>PoseSettingsForm.cs</DependentUpon>
     </EmbeddedResource>
@@ -1082,6 +1413,9 @@
     <EmbeddedResource Include="Forms\SituationLinker.resx">
       <DependentUpon>SituationLinker.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Forms\StageImageSelection.resx">
+      <DependentUpon>StageImageSelection.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Forms\StageSelect.resx">
       <DependentUpon>StageSelect.cs</DependentUpon>
     </EmbeddedResource>
@@ -1129,6 +1463,45 @@
       <DependentUpon>Settings.settings</DependentUpon>
       <DesignTimeSharedInput>True</DesignTimeSharedInput>
     </Compile>
+    <None Include="Resources\Skins\Black Forest.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\Mint Chocolate.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\Purple.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\Blue.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\Classic.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\Dark Mode.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\Dark Rose.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\Deep Sea.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\Florina.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\Forest.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\Mandevilla.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\Morning Rays.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="Resources\Skins\The Inventory.skin">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <None Include="App.config" />
@@ -1136,6 +1509,21 @@
   <ItemGroup>
     <None Include="Cursors\hand_closed.cur" />
     <None Include="Cursors\hand_open.cur" />
+    <Content Include="Help\conditions.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Help\images\nickname.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Help\images\slicer.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Help\images\text_formatting.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Help\line_slicer.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="Help\data_analyzer.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -1160,6 +1548,9 @@
     <Content Include="Help\images\collectible_unlock.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="Help\images\layout.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="Help\images\tut1_sequence.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -1248,22 +1639,22 @@
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="Help\images\tut2_copy.png">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="Help\images\tut2_final.png">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="Help\images\tut2_loop.png">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="Help\images\tut2_move_time.png">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="Help\images\tut2_screen1.png">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="Help\images\tut2_wing.png">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="Help\images\tut3_bounce.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -1313,6 +1704,9 @@
     <Content Include="Help\pose_tutorial4.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="Help\advanced.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="Help\tutorial_template.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -1433,6 +1827,76 @@
     <None Include="Icons\BookmarkUnfilled.png" />
     <None Include="Icons\ColorPalette.png" />
     <None Include="Icons\Ellipsis.png" />
+    <None Include="Icons\Refresh.png" />
+    <None Include="Icons\Image.png" />
+    <None Include="Icons\GIF.png" />
+    <None Include="Icons\Legend.png" />
+    <None Include="Icons\Recipe.png" />
+    <None Include="Icons\Settings.png" />
+    <None Include="Icons\Theme.png" />
+    <None Include="Icons\Sort.png" />
+    <None Include="Icons\AddPause.png" />
+    <None Include="Icons\SpeechBubble.png" />
+    <Content Include="Resources\license.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\preview.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\15f3a04f-8e03-40e0-b426-dcba8b4f5b3b.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\1ac2b058-5e06-4321-9ea7-d6bc3098748e.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\40a78c86-ea36-42da-b84b-039a1e3c2a9c.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\43e68bf4-4dc8-4938-a5d7-8f1e17e1202e.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\4df79a02-26c8-4fa7-b7cd-e8377533ed58.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\5343e2e7-3c56-477d-b248-332021a916e1.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\5eced77a-bd5e-4d35-8a1b-a979a1c73250.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\6091088d-2c85-4d55-a5f3-05bab45351a8.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\670e7368-4059-4a56-8171-65d18d43ebff.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\70dd1e4f-4d07-4b44-947d-57b494522f40.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\797c82ff-421e-4c29-a950-094bca5a147d.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\8338b475-6f72-461a-9cb7-4510428e1297.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\841c009f-617e-47e1-b1dd-214ba5892e88.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\93fb1799-f582-4f08-a53c-49d5e56f6278.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\ab63638e-fcc5-44e9-b020-a677785bb36f.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\b90f559d-7557-4180-989e-63b583b9a82d.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\dba5778a-dbed-4079-9052-d204275d6f93.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\Recipes\eb570363-dbe2-45bb-ac47-916fc820de42.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="Resources\words.txt">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -1455,9 +1919,6 @@
     <Content Include="Help\faq.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="Help\file.html">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
     <Content Include="Help\help.css">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -1533,6 +1994,9 @@
     <Content Include="VersionHistory\v4.2.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="VersionHistory\v5.0.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="VersionHistory\v4.3.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -1601,6 +2065,7 @@
       <Install>false</Install>
     </BootstrapperPackage>
   </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
        Other similar extension points exist, see Microsoft.Common.targets.
diff --git a/editor source/SPNATI Character Editor/Serialization/ImportEdit.cs b/editor source/SPNATI Character Editor/Serialization/ImportEdit.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d85f1904b163c4c2d613652c39989c83db1cdfbf
--- /dev/null
+++ b/editor source/SPNATI Character Editor/Serialization/ImportEdit.cs	
@@ -0,0 +1,184 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+
+namespace SPNATI_Character_Editor
+{
+	[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+	public class ImportEdit
+	{
+		[JsonProperty("type")]
+		public string Mode;
+
+		[JsonProperty("intent")]
+		public string Intent;
+
+		[JsonProperty("stage")]
+		public int Stage;
+
+		[JsonProperty("case")]
+		public Case SourceCase;
+
+		[JsonProperty("oldState")]
+		public ImportState OriginalState;
+
+		[JsonProperty("state")]
+		public ImportState State;
+
+		[JsonProperty("suggestedTag")]
+		public string SuggestedTag;
+
+		[JsonProperty("responseTarget")]
+		public ImportTarget ResponseTarget;
+
+		public ImportEdit() { }
+
+		/// <summary>
+		/// Creates a case for the given character using the information in this edit
+		/// </summary>
+		/// <param name="character">Character for which to make the case</param>
+		/// <returns></returns>
+		public Case CreateCase(Character character)
+		{
+			Case result = null;
+			switch (Mode)
+			{
+				case "edit":
+					if (OriginalState != null)
+					{
+						OriginalState.Text = OriginalState.Text.Replace("&lt;", "<");
+						OriginalState.Text = OriginalState.Text.Replace("&gt;", ">");
+					}
+					Case workingCase = FindMatchingWorkingCase(character);
+					if (workingCase != null)
+					{
+						DialogueLine line = workingCase.Lines.Find(l => l.Text == OriginalState.Text);
+						if (line != null)
+						{
+							line.Text = State.Text;
+							if (!string.IsNullOrEmpty(State.Marker?.ToString()))
+							{
+								line.Marker = State.Marker.ToString();
+							}
+							line.Image = State.Image;
+							line.GeneralizeImage(line);
+							result = workingCase;
+						}
+						else
+						{
+							throw new ImportLinesException($"No line matching '{OriginalState.Text}' found.");
+						}
+					}
+					else
+					{
+						throw new ImportLinesException("Couldn't find an existing case to edit.");
+					}
+					break;
+				case "new":
+					if (Intent == "response")
+					{
+						Character speaker = CharacterDatabase.Get(ResponseTarget.Id);
+						if (speaker == null)
+						{
+							throw new ImportLinesException($"Unrecognized response target: {ResponseTarget.Id}");
+						}
+						Case response = ResponseTarget.Case.CreateResponse(speaker, character);
+						DialogueLine caseLine = new DialogueLine(State.Image, State.Text);
+						caseLine.GeneralizeImage(caseLine);
+						response.Lines.Add(caseLine);
+						result = response;
+					}
+					else if (Intent == "generic")
+					{
+						workingCase = new Case(SuggestedTag);
+						workingCase.Stages.Add(Stage);
+						DialogueLine caseLine = new DialogueLine(State.Image, State.Text);
+						caseLine.GeneralizeImage(caseLine);
+						workingCase.Lines.Add(caseLine);
+						result = workingCase;
+					}
+					else
+					{
+						throw new ImportLinesException($"Unknown intent: {Intent}");
+					}
+					character.Behavior.AddWorkingCase(result);
+					break;
+				default:
+					throw new ImportLinesException($"Unrecognized type: {Mode}");
+			}
+			return result;
+		}
+
+		/// <summary>
+		/// Finds a working case that matches the conditions of this edit
+		/// </summary>
+		/// <param name="character"></param>
+		/// <returns></returns>
+		private Case FindMatchingWorkingCase(Character character)
+		{
+			foreach (Case workingCase in character.Behavior.GetWorkingCases())
+			{
+				if (workingCase.MatchesConditions(SourceCase) && workingCase.MatchesStages(SourceCase, false))
+				{
+					return workingCase;
+				}
+			}
+			return null;
+		}
+	}
+
+	public class ImportStage
+	{
+		[JsonProperty("min")]
+		public int Min;
+
+		[JsonProperty("max")]
+		public int Max;
+	}
+
+	public class ImportState
+	{
+		[JsonProperty("text")]
+		public string Text;
+
+		[JsonProperty("image")]
+		public string Image;
+
+		[JsonProperty("marker")]
+		public ImportMarker Marker;
+	}
+
+	public class ImportTarget
+	{
+		[JsonProperty("id")]
+		public string Id;
+
+		[JsonProperty("stage")]
+		public int Stage;
+
+		[JsonProperty("case")]
+		public Case Case;
+
+		[JsonProperty("state")]
+		public ImportState State;
+	}
+
+	public class ImportMarker
+	{
+		[JsonProperty("name")]
+		public string Name;
+
+		[JsonProperty("perTarget")]
+		public string PerTarget;
+
+		[JsonProperty("Value")]
+		public string Value;
+	}
+
+	public class ImportLinesException : Exception
+	{
+		public ImportLinesException(string msg) : base(msg)
+		{
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/SettingsSetup.Designer.cs b/editor source/SPNATI Character Editor/SettingsSetup.Designer.cs
index 544c85dd7df49e7429971ebecff1035091af8433..7839aad06f08f88cf5ffdd52f86cc6fb0677ec71 100644
--- a/editor source/SPNATI Character Editor/SettingsSetup.Designer.cs	
+++ b/editor source/SPNATI Character Editor/SettingsSetup.Designer.cs	
@@ -30,55 +30,75 @@
 		{
 			this.components = new System.ComponentModel.Container();
 			this.folderBrowserDialog1 = new System.Windows.Forms.FolderBrowserDialog();
-			this.txtApplicationDirectory = new System.Windows.Forms.TextBox();
-			this.label1 = new System.Windows.Forms.Label();
-			this.cmdOk = new System.Windows.Forms.Button();
-			this.cmdBrowse = new System.Windows.Forms.Button();
-			this.cmdCancel = new System.Windows.Forms.Button();
-			this.label2 = new System.Windows.Forms.Label();
-			this.txtUserName = new System.Windows.Forms.TextBox();
+			this.txtApplicationDirectory = new Desktop.Skinning.SkinnedTextBox();
+			this.label1 = new Desktop.Skinning.SkinnedLabel();
+			this.cmdOk = new Desktop.Skinning.SkinnedButton();
+			this.cmdBrowse = new Desktop.Skinning.SkinnedButton();
+			this.cmdCancel = new Desktop.Skinning.SkinnedButton();
+			this.label2 = new Desktop.Skinning.SkinnedLabel();
+			this.txtUserName = new Desktop.Skinning.SkinnedTextBox();
 			this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
-			this.label3 = new System.Windows.Forms.Label();
-			this.valAutoSave = new System.Windows.Forms.NumericUpDown();
+			this.label3 = new Desktop.Skinning.SkinnedLabel();
+			this.valAutoSave = new Desktop.Skinning.SkinnedNumericUpDown();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
-			this.chkHideImages = new System.Windows.Forms.CheckBox();
-			this.helpAutoSave = new System.Windows.Forms.Button();
-			this.helpIntellisense = new System.Windows.Forms.Button();
-			this.button1 = new System.Windows.Forms.Button();
-			this.chkDefaults = new System.Windows.Forms.CheckBox();
-			this.chkIntellisense = new System.Windows.Forms.CheckBox();
-			this.label4 = new System.Windows.Forms.Label();
-			this.txtFilter = new System.Windows.Forms.TextBox();
-			this.tabsSections = new System.Windows.Forms.TabControl();
+			this.chkHidePrefixlessImages = new Desktop.Skinning.SkinnedCheckBox();
+			this.helpAutoSave = new Desktop.Skinning.SkinnedIcon();
+			this.helpIntellisense = new Desktop.Skinning.SkinnedIcon();
+			this.button1 = new Desktop.Skinning.SkinnedIcon();
+			this.chkDefaults = new Desktop.Skinning.SkinnedCheckBox();
+			this.chkIntellisense = new Desktop.Skinning.SkinnedCheckBox();
+			this.label4 = new Desktop.Skinning.SkinnedLabel();
+			this.txtFilter = new Desktop.Skinning.SkinnedTextBox();
+			this.tabsSections = new Desktop.Skinning.SkinnedTabControl();
 			this.tabGeneral = new System.Windows.Forms.TabPage();
-			this.cmdBrowseKisekae = new System.Windows.Forms.Button();
-			this.txtKisekae = new System.Windows.Forms.TextBox();
-			this.label5 = new System.Windows.Forms.Label();
-			this.chkAutoBackup = new System.Windows.Forms.CheckBox();
+			this.chkHideImages = new Desktop.Skinning.SkinnedCheckBox();
+			this.cmdBrowseKisekae = new Desktop.Skinning.SkinnedButton();
+			this.txtKisekae = new Desktop.Skinning.SkinnedTextBox();
+			this.label5 = new Desktop.Skinning.SkinnedLabel();
 			this.tabDialogue = new System.Windows.Forms.TabPage();
-			this.chkInitialAdd = new System.Windows.Forms.CheckBox();
+			this.chkColorTargets = new Desktop.Skinning.SkinnedCheckBox();
+			this.chkCaseTree = new Desktop.Skinning.SkinnedCheckBox();
+			this.chkInitialAdd = new Desktop.Skinning.SkinnedCheckBox();
 			this.tabBanter = new System.Windows.Forms.TabPage();
-			this.chkAutoBanter = new System.Windows.Forms.CheckBox();
+			this.chkAutoBanter = new Desktop.Skinning.SkinnedCheckBox();
+			this.tabEpilogues = new System.Windows.Forms.TabPage();
+			this.label6 = new Desktop.Skinning.SkinnedLabel();
+			this.lstPauses = new Desktop.Skinning.SkinnedCheckedListBox();
+			this.stripSections = new Desktop.Skinning.SkinnedTabStrip();
+			this.skinnedPanel1 = new Desktop.Skinning.SkinnedPanel();
+			this.tabTroubleshoot = new System.Windows.Forms.TabPage();
+			this.chkAutoBackup = new Desktop.Skinning.SkinnedCheckBox();
+			this.chkWorkflowTracer = new Desktop.Skinning.SkinnedCheckBox();
 			((System.ComponentModel.ISupportInitialize)(this.valAutoSave)).BeginInit();
 			this.tabsSections.SuspendLayout();
 			this.tabGeneral.SuspendLayout();
 			this.tabDialogue.SuspendLayout();
 			this.tabBanter.SuspendLayout();
+			this.tabEpilogues.SuspendLayout();
+			this.skinnedPanel1.SuspendLayout();
+			this.tabTroubleshoot.SuspendLayout();
 			this.SuspendLayout();
 			// 
 			// txtApplicationDirectory
 			// 
 			this.txtApplicationDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.txtApplicationDirectory.Location = new System.Drawing.Point(116, 8);
+			this.txtApplicationDirectory.BackColor = System.Drawing.Color.White;
+			this.txtApplicationDirectory.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtApplicationDirectory.ForeColor = System.Drawing.Color.Black;
+			this.txtApplicationDirectory.Location = new System.Drawing.Point(133, 8);
 			this.txtApplicationDirectory.Name = "txtApplicationDirectory";
-			this.txtApplicationDirectory.Size = new System.Drawing.Size(353, 20);
+			this.txtApplicationDirectory.Size = new System.Drawing.Size(238, 20);
 			this.txtApplicationDirectory.TabIndex = 0;
 			this.txtApplicationDirectory.Validating += new System.ComponentModel.CancelEventHandler(this.txtApplicationDirectory_Validating);
 			// 
 			// label1
 			// 
 			this.label1.AutoSize = true;
+			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label1.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label1.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label1.Location = new System.Drawing.Point(6, 11);
 			this.label1.Name = "label1";
 			this.label1.Size = new System.Drawing.Size(97, 13);
@@ -88,7 +108,11 @@
 			// cmdOk
 			// 
 			this.cmdOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdOk.Location = new System.Drawing.Point(457, 178);
+			this.cmdOk.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdOk.FieldType = Desktop.Skinning.SkinnedFieldType.Secondary;
+			this.cmdOk.Flat = false;
+			this.cmdOk.ForeColor = System.Drawing.Color.White;
+			this.cmdOk.Location = new System.Drawing.Point(498, 4);
 			this.cmdOk.Name = "cmdOk";
 			this.cmdOk.Size = new System.Drawing.Size(75, 23);
 			this.cmdOk.TabIndex = 4;
@@ -99,7 +123,10 @@
 			// cmdBrowse
 			// 
 			this.cmdBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdBrowse.Location = new System.Drawing.Point(475, 6);
+			this.cmdBrowse.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdBrowse.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdBrowse.Flat = false;
+			this.cmdBrowse.Location = new System.Drawing.Point(377, 6);
 			this.cmdBrowse.Name = "cmdBrowse";
 			this.cmdBrowse.Size = new System.Drawing.Size(32, 23);
 			this.cmdBrowse.TabIndex = 1;
@@ -110,8 +137,12 @@
 			// cmdCancel
 			// 
 			this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+			this.cmdCancel.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
 			this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cmdCancel.Location = new System.Drawing.Point(538, 178);
+			this.cmdCancel.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdCancel.Flat = true;
+			this.cmdCancel.ForeColor = System.Drawing.Color.White;
+			this.cmdCancel.Location = new System.Drawing.Point(579, 4);
 			this.cmdCancel.Name = "cmdCancel";
 			this.cmdCancel.Size = new System.Drawing.Size(75, 23);
 			this.cmdCancel.TabIndex = 5;
@@ -122,6 +153,10 @@
 			// label2
 			// 
 			this.label2.AutoSize = true;
+			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label2.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label2.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label2.Location = new System.Drawing.Point(7, 63);
 			this.label2.Name = "label2";
 			this.label2.Size = new System.Drawing.Size(58, 13);
@@ -132,9 +167,12 @@
 			// 
 			this.txtUserName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.txtUserName.Location = new System.Drawing.Point(116, 60);
+			this.txtUserName.BackColor = System.Drawing.Color.White;
+			this.txtUserName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtUserName.ForeColor = System.Drawing.Color.Black;
+			this.txtUserName.Location = new System.Drawing.Point(133, 60);
 			this.txtUserName.Name = "txtUserName";
-			this.txtUserName.Size = new System.Drawing.Size(391, 20);
+			this.txtUserName.Size = new System.Drawing.Size(276, 20);
 			this.txtUserName.TabIndex = 4;
 			this.toolTip1.SetToolTip(this.txtUserName, "This is used for auto-saving. Only characters written by this user will be auto-s" +
         "aved.");
@@ -147,6 +185,10 @@
 			// label3
 			// 
 			this.label3.AutoSize = true;
+			this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label3.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label3.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label3.Location = new System.Drawing.Point(7, 88);
 			this.label3.Name = "label3";
 			this.label3.Size = new System.Drawing.Size(103, 13);
@@ -155,7 +197,10 @@
 			// 
 			// valAutoSave
 			// 
-			this.valAutoSave.Location = new System.Drawing.Point(116, 86);
+			this.valAutoSave.BackColor = System.Drawing.Color.White;
+			this.valAutoSave.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.valAutoSave.ForeColor = System.Drawing.Color.Black;
+			this.valAutoSave.Location = new System.Drawing.Point(133, 85);
 			this.valAutoSave.Maximum = new decimal(new int[] {
             60,
             0,
@@ -167,53 +212,63 @@
 			this.toolTip1.SetToolTip(this.valAutoSave, "Number of minutes to auto-save characters you\'ve written. Use 0 to disable auto-s" +
         "ave.");
 			// 
-			// chkHideImages
+			// chkHidePrefixlessImages
 			// 
-			this.chkHideImages.AutoSize = true;
-			this.chkHideImages.Location = new System.Drawing.Point(6, 29);
-			this.chkHideImages.Name = "chkHideImages";
-			this.chkHideImages.Size = new System.Drawing.Size(143, 17);
-			this.chkHideImages.TabIndex = 9;
-			this.chkHideImages.Text = "Include prefixless images";
-			this.toolTip1.SetToolTip(this.chkHideImages, "If unchecked, images with no prefix (ex. 0-*.png) will not appear for use in dial" +
+			this.chkHidePrefixlessImages.AutoSize = true;
+			this.chkHidePrefixlessImages.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkHidePrefixlessImages.Location = new System.Drawing.Point(6, 29);
+			this.chkHidePrefixlessImages.Name = "chkHidePrefixlessImages";
+			this.chkHidePrefixlessImages.Size = new System.Drawing.Size(143, 17);
+			this.chkHidePrefixlessImages.TabIndex = 22;
+			this.chkHidePrefixlessImages.Text = "Include prefixless images";
+			this.toolTip1.SetToolTip(this.chkHidePrefixlessImages, "If unchecked, images with no prefix (ex. 0-*.png) will not appear for use in dial" +
         "ogue lines.");
-			this.chkHideImages.UseVisualStyleBackColor = true;
-			this.chkHideImages.CheckedChanged += new System.EventHandler(this.chkHideImages_CheckedChanged);
+			this.chkHidePrefixlessImages.UseVisualStyleBackColor = true;
+			this.chkHidePrefixlessImages.CheckedChanged += new System.EventHandler(this.chkHideImages_CheckedChanged);
 			// 
 			// helpAutoSave
 			// 
+			this.helpAutoSave.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.helpAutoSave.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.helpAutoSave.Flat = false;
 			this.helpAutoSave.FlatAppearance.BorderSize = 0;
 			this.helpAutoSave.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
 			this.helpAutoSave.Image = global::SPNATI_Character_Editor.Properties.Resources.Help;
-			this.helpAutoSave.Location = new System.Drawing.Point(167, 83);
+			this.helpAutoSave.Location = new System.Drawing.Point(182, 83);
 			this.helpAutoSave.Name = "helpAutoSave";
-			this.helpAutoSave.Size = new System.Drawing.Size(26, 23);
+			this.helpAutoSave.Size = new System.Drawing.Size(21, 23);
 			this.helpAutoSave.TabIndex = 6;
 			this.toolTip1.SetToolTip(this.helpAutoSave, "Only characters with your username as the writer can be auto-saved");
 			this.helpAutoSave.UseVisualStyleBackColor = true;
 			// 
 			// helpIntellisense
 			// 
+			this.helpIntellisense.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.helpIntellisense.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.helpIntellisense.Flat = false;
 			this.helpIntellisense.FlatAppearance.BorderSize = 0;
 			this.helpIntellisense.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
 			this.helpIntellisense.Image = global::SPNATI_Character_Editor.Properties.Resources.Help;
-			this.helpIntellisense.Location = new System.Drawing.Point(149, 2);
+			this.helpIntellisense.Location = new System.Drawing.Point(145, 2);
 			this.helpIntellisense.Name = "helpIntellisense";
 			this.helpIntellisense.Size = new System.Drawing.Size(26, 23);
-			this.helpIntellisense.TabIndex = 12;
+			this.helpIntellisense.TabIndex = 21;
 			this.toolTip1.SetToolTip(this.helpIntellisense, "Typing ~ in a dialogue line will bring up a popup containing available variables," +
         " their meanings, parameters, and so on.");
 			this.helpIntellisense.UseVisualStyleBackColor = true;
 			// 
 			// button1
 			// 
+			this.button1.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.button1.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.button1.Flat = false;
 			this.button1.FlatAppearance.BorderSize = 0;
 			this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
 			this.button1.Image = global::SPNATI_Character_Editor.Properties.Resources.Help;
-			this.button1.Location = new System.Drawing.Point(149, 25);
+			this.button1.Location = new System.Drawing.Point(145, 25);
 			this.button1.Name = "button1";
 			this.button1.Size = new System.Drawing.Size(26, 23);
-			this.button1.TabIndex = 13;
+			this.button1.TabIndex = 23;
 			this.toolTip1.SetToolTip(this.button1, "When checked, every image in the character\'s folder with no stage prefix (ex. 2-h" +
         "appy.png) will be available for poses in every stage.");
 			this.button1.UseVisualStyleBackColor = true;
@@ -221,10 +276,11 @@
 			// chkDefaults
 			// 
 			this.chkDefaults.AutoSize = true;
+			this.chkDefaults.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
 			this.chkDefaults.Location = new System.Drawing.Point(6, 97);
 			this.chkDefaults.Name = "chkDefaults";
 			this.chkDefaults.Size = new System.Drawing.Size(179, 17);
-			this.chkDefaults.TabIndex = 15;
+			this.chkDefaults.TabIndex = 26;
 			this.chkDefaults.Text = "Ensure cases have generic lines";
 			this.toolTip1.SetToolTip(this.chkDefaults, "If unchecked, images with no prefix (ex. 0-*.png) will not appear for use in dial" +
         "ogue lines.");
@@ -233,16 +289,21 @@
 			// chkIntellisense
 			// 
 			this.chkIntellisense.AutoSize = true;
+			this.chkIntellisense.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
 			this.chkIntellisense.Location = new System.Drawing.Point(6, 6);
 			this.chkIntellisense.Name = "chkIntellisense";
 			this.chkIntellisense.Size = new System.Drawing.Size(139, 17);
-			this.chkIntellisense.TabIndex = 8;
+			this.chkIntellisense.TabIndex = 20;
 			this.chkIntellisense.Text = "Use variable intellisense";
 			this.chkIntellisense.UseVisualStyleBackColor = true;
 			// 
 			// label4
 			// 
 			this.label4.AutoSize = true;
+			this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label4.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label4.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label4.Location = new System.Drawing.Point(3, 53);
 			this.label4.Name = "label4";
 			this.label4.Size = new System.Drawing.Size(143, 13);
@@ -251,10 +312,13 @@
 			// 
 			// txtFilter
 			// 
-			this.txtFilter.Location = new System.Drawing.Point(155, 50);
+			this.txtFilter.BackColor = System.Drawing.Color.White;
+			this.txtFilter.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtFilter.ForeColor = System.Drawing.Color.Black;
+			this.txtFilter.Location = new System.Drawing.Point(145, 50);
 			this.txtFilter.Name = "txtFilter";
 			this.txtFilter.Size = new System.Drawing.Size(92, 20);
-			this.txtFilter.TabIndex = 11;
+			this.txtFilter.TabIndex = 24;
 			// 
 			// tabsSections
 			// 
@@ -265,24 +329,25 @@
 			this.tabsSections.Controls.Add(this.tabGeneral);
 			this.tabsSections.Controls.Add(this.tabDialogue);
 			this.tabsSections.Controls.Add(this.tabBanter);
-			this.tabsSections.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed;
+			this.tabsSections.Controls.Add(this.tabEpilogues);
+			this.tabsSections.Controls.Add(this.tabTroubleshoot);
 			this.tabsSections.ItemSize = new System.Drawing.Size(25, 100);
-			this.tabsSections.Location = new System.Drawing.Point(2, 2);
+			this.tabsSections.Location = new System.Drawing.Point(101, 27);
+			this.tabsSections.Margin = new System.Windows.Forms.Padding(0);
 			this.tabsSections.Multiline = true;
 			this.tabsSections.Name = "tabsSections";
 			this.tabsSections.SelectedIndex = 0;
-			this.tabsSections.Size = new System.Drawing.Size(621, 170);
+			this.tabsSections.Size = new System.Drawing.Size(555, 181);
 			this.tabsSections.SizeMode = System.Windows.Forms.TabSizeMode.Fixed;
 			this.tabsSections.TabIndex = 12;
-			this.tabsSections.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.tabsSections_DrawItem);
 			// 
 			// tabGeneral
 			// 
-			this.tabGeneral.BackColor = System.Drawing.SystemColors.Control;
+			this.tabGeneral.BackColor = System.Drawing.Color.White;
+			this.tabGeneral.Controls.Add(this.chkHideImages);
 			this.tabGeneral.Controls.Add(this.cmdBrowseKisekae);
 			this.tabGeneral.Controls.Add(this.txtKisekae);
 			this.tabGeneral.Controls.Add(this.label5);
-			this.tabGeneral.Controls.Add(this.chkAutoBackup);
 			this.tabGeneral.Controls.Add(this.helpAutoSave);
 			this.tabGeneral.Controls.Add(this.cmdBrowse);
 			this.tabGeneral.Controls.Add(this.txtApplicationDirectory);
@@ -291,17 +356,33 @@
 			this.tabGeneral.Controls.Add(this.valAutoSave);
 			this.tabGeneral.Controls.Add(this.label2);
 			this.tabGeneral.Controls.Add(this.label3);
+			this.tabGeneral.ForeColor = System.Drawing.Color.Black;
 			this.tabGeneral.Location = new System.Drawing.Point(104, 4);
 			this.tabGeneral.Name = "tabGeneral";
 			this.tabGeneral.Padding = new System.Windows.Forms.Padding(3);
-			this.tabGeneral.Size = new System.Drawing.Size(513, 162);
+			this.tabGeneral.Size = new System.Drawing.Size(447, 173);
 			this.tabGeneral.TabIndex = 0;
 			this.tabGeneral.Text = "General";
 			// 
+			// chkHideImages
+			// 
+			this.chkHideImages.AutoSize = true;
+			this.chkHideImages.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkHideImages.Location = new System.Drawing.Point(10, 111);
+			this.chkHideImages.Name = "chkHideImages";
+			this.chkHideImages.Size = new System.Drawing.Size(126, 17);
+			this.chkHideImages.TabIndex = 9;
+			this.chkHideImages.Text = "Hide Preview Images";
+			this.chkHideImages.UseVisualStyleBackColor = true;
+			this.chkHideImages.CheckedChanged += new System.EventHandler(this.chkHideImages_CheckedChanged_1);
+			// 
 			// cmdBrowseKisekae
 			// 
 			this.cmdBrowseKisekae.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.cmdBrowseKisekae.Location = new System.Drawing.Point(475, 32);
+			this.cmdBrowseKisekae.Background = Desktop.Skinning.SkinnedBackgroundType.Surface;
+			this.cmdBrowseKisekae.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.cmdBrowseKisekae.Flat = false;
+			this.cmdBrowseKisekae.Location = new System.Drawing.Point(377, 32);
 			this.cmdBrowseKisekae.Name = "cmdBrowseKisekae";
 			this.cmdBrowseKisekae.Size = new System.Drawing.Size(32, 23);
 			this.cmdBrowseKisekae.TabIndex = 3;
@@ -313,72 +394,97 @@
 			// 
 			this.txtKisekae.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-			this.txtKisekae.Location = new System.Drawing.Point(116, 34);
+			this.txtKisekae.BackColor = System.Drawing.Color.White;
+			this.txtKisekae.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.txtKisekae.ForeColor = System.Drawing.Color.Black;
+			this.txtKisekae.Location = new System.Drawing.Point(133, 34);
 			this.txtKisekae.Name = "txtKisekae";
-			this.txtKisekae.Size = new System.Drawing.Size(353, 20);
+			this.txtKisekae.Size = new System.Drawing.Size(238, 20);
 			this.txtKisekae.TabIndex = 2;
 			// 
 			// label5
 			// 
 			this.label5.AutoSize = true;
+			this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label5.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label5.Highlight = Desktop.Skinning.SkinnedHighlight.Label;
+			this.label5.Level = Desktop.Skinning.SkinnedLabelLevel.Normal;
 			this.label5.Location = new System.Drawing.Point(7, 37);
 			this.label5.Name = "label5";
 			this.label5.Size = new System.Drawing.Size(90, 13);
 			this.label5.TabIndex = 9;
 			this.label5.Text = "KKL.exe location:";
 			// 
-			// chkAutoBackup
-			// 
-			this.chkAutoBackup.AutoSize = true;
-			this.chkAutoBackup.Location = new System.Drawing.Point(250, 87);
-			this.chkAutoBackup.Name = "chkAutoBackup";
-			this.chkAutoBackup.Size = new System.Drawing.Size(176, 17);
-			this.chkAutoBackup.TabIndex = 7;
-			this.chkAutoBackup.Text = "Create data recovery snapshots";
-			this.chkAutoBackup.UseVisualStyleBackColor = true;
-			// 
 			// tabDialogue
 			// 
-			this.tabDialogue.BackColor = System.Drawing.SystemColors.Control;
+			this.tabDialogue.BackColor = System.Drawing.Color.White;
+			this.tabDialogue.Controls.Add(this.chkColorTargets);
+			this.tabDialogue.Controls.Add(this.chkCaseTree);
 			this.tabDialogue.Controls.Add(this.chkDefaults);
 			this.tabDialogue.Controls.Add(this.chkInitialAdd);
 			this.tabDialogue.Controls.Add(this.button1);
 			this.tabDialogue.Controls.Add(this.helpIntellisense);
 			this.tabDialogue.Controls.Add(this.chkIntellisense);
 			this.tabDialogue.Controls.Add(this.txtFilter);
-			this.tabDialogue.Controls.Add(this.chkHideImages);
+			this.tabDialogue.Controls.Add(this.chkHidePrefixlessImages);
 			this.tabDialogue.Controls.Add(this.label4);
+			this.tabDialogue.ForeColor = System.Drawing.Color.Black;
 			this.tabDialogue.Location = new System.Drawing.Point(104, 4);
 			this.tabDialogue.Name = "tabDialogue";
 			this.tabDialogue.Padding = new System.Windows.Forms.Padding(3);
-			this.tabDialogue.Size = new System.Drawing.Size(513, 162);
+			this.tabDialogue.Size = new System.Drawing.Size(447, 173);
 			this.tabDialogue.TabIndex = 1;
 			this.tabDialogue.Text = "Dialogue";
 			// 
+			// chkColorTargets
+			// 
+			this.chkColorTargets.AutoSize = true;
+			this.chkColorTargets.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkColorTargets.Location = new System.Drawing.Point(6, 143);
+			this.chkColorTargets.Name = "chkColorTargets";
+			this.chkColorTargets.Size = new System.Drawing.Size(239, 17);
+			this.chkColorTargets.TabIndex = 28;
+			this.chkColorTargets.Text = "Color code lines that target another character";
+			this.chkColorTargets.UseVisualStyleBackColor = true;
+			// 
+			// chkCaseTree
+			// 
+			this.chkCaseTree.AutoSize = true;
+			this.chkCaseTree.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkCaseTree.Location = new System.Drawing.Point(6, 120);
+			this.chkCaseTree.Name = "chkCaseTree";
+			this.chkCaseTree.Size = new System.Drawing.Size(230, 17);
+			this.chkCaseTree.TabIndex = 27;
+			this.chkCaseTree.Text = "Use multi-column case tree (restart needed)";
+			this.chkCaseTree.UseVisualStyleBackColor = true;
+			// 
 			// chkInitialAdd
 			// 
 			this.chkInitialAdd.AutoSize = true;
+			this.chkInitialAdd.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
 			this.chkInitialAdd.Location = new System.Drawing.Point(6, 74);
 			this.chkInitialAdd.Name = "chkInitialAdd";
 			this.chkInitialAdd.Size = new System.Drawing.Size(258, 17);
-			this.chkInitialAdd.TabIndex = 14;
+			this.chkInitialAdd.TabIndex = 25;
 			this.chkInitialAdd.Text = "Auto-open selection form when adding conditions";
 			this.chkInitialAdd.UseVisualStyleBackColor = true;
 			// 
 			// tabBanter
 			// 
-			this.tabBanter.BackColor = System.Drawing.SystemColors.Control;
+			this.tabBanter.BackColor = System.Drawing.Color.White;
 			this.tabBanter.Controls.Add(this.chkAutoBanter);
+			this.tabBanter.ForeColor = System.Drawing.Color.Black;
 			this.tabBanter.Location = new System.Drawing.Point(104, 4);
 			this.tabBanter.Name = "tabBanter";
 			this.tabBanter.Padding = new System.Windows.Forms.Padding(3);
-			this.tabBanter.Size = new System.Drawing.Size(513, 162);
+			this.tabBanter.Size = new System.Drawing.Size(447, 173);
 			this.tabBanter.TabIndex = 2;
 			this.tabBanter.Text = "Banter Wizard";
 			// 
 			// chkAutoBanter
 			// 
 			this.chkAutoBanter.AutoSize = true;
+			this.chkAutoBanter.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
 			this.chkAutoBanter.Location = new System.Drawing.Point(6, 6);
 			this.chkAutoBanter.Name = "chkAutoBanter";
 			this.chkAutoBanter.Size = new System.Drawing.Size(383, 17);
@@ -387,19 +493,131 @@
     "d)";
 			this.chkAutoBanter.UseVisualStyleBackColor = true;
 			// 
+			// tabEpilogues
+			// 
+			this.tabEpilogues.BackColor = System.Drawing.Color.White;
+			this.tabEpilogues.Controls.Add(this.label6);
+			this.tabEpilogues.Controls.Add(this.lstPauses);
+			this.tabEpilogues.ForeColor = System.Drawing.Color.Black;
+			this.tabEpilogues.Location = new System.Drawing.Point(104, 4);
+			this.tabEpilogues.Name = "tabEpilogues";
+			this.tabEpilogues.Padding = new System.Windows.Forms.Padding(3);
+			this.tabEpilogues.Size = new System.Drawing.Size(447, 173);
+			this.tabEpilogues.TabIndex = 3;
+			this.tabEpilogues.Text = "Epilogues";
+			// 
+			// label6
+			// 
+			this.label6.AutoSize = true;
+			this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.label6.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(127)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
+			this.label6.Highlight = Desktop.Skinning.SkinnedHighlight.Normal;
+			this.label6.Level = Desktop.Skinning.SkinnedLabelLevel.Label;
+			this.label6.Location = new System.Drawing.Point(6, 7);
+			this.label6.Name = "label6";
+			this.label6.Size = new System.Drawing.Size(237, 13);
+			this.label6.TabIndex = 1;
+			this.label6.Text = "Auto-add \"Wait for Input\" directive when adding:";
+			// 
+			// lstPauses
+			// 
+			this.lstPauses.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+			this.lstPauses.BackColor = System.Drawing.Color.White;
+			this.lstPauses.CheckOnClick = true;
+			this.lstPauses.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
+			this.lstPauses.ForeColor = System.Drawing.Color.Black;
+			this.lstPauses.FormattingEnabled = true;
+			this.lstPauses.Location = new System.Drawing.Point(6, 23);
+			this.lstPauses.Name = "lstPauses";
+			this.lstPauses.Size = new System.Drawing.Size(120, 124);
+			this.lstPauses.TabIndex = 0;
+			// 
+			// stripSections
+			// 
+			this.stripSections.AddCaption = null;
+			this.stripSections.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+			this.stripSections.Location = new System.Drawing.Point(1, 27);
+			this.stripSections.Margin = new System.Windows.Forms.Padding(0);
+			this.stripSections.Name = "stripSections";
+			this.stripSections.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryLight;
+			this.stripSections.ShowAddButton = false;
+			this.stripSections.ShowCloseButton = false;
+			this.stripSections.Size = new System.Drawing.Size(100, 181);
+			this.stripSections.StartMargin = 5;
+			this.stripSections.TabControl = this.tabsSections;
+			this.stripSections.TabIndex = 13;
+			this.stripSections.TabMargin = 5;
+			this.stripSections.TabPadding = 20;
+			this.stripSections.TabSize = 25;
+			this.stripSections.TabType = Desktop.Skinning.SkinnedBackgroundType.Background;
+			this.stripSections.Vertical = true;
+			// 
+			// skinnedPanel1
+			// 
+			this.skinnedPanel1.Controls.Add(this.cmdCancel);
+			this.skinnedPanel1.Controls.Add(this.cmdOk);
+			this.skinnedPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+			this.skinnedPanel1.Location = new System.Drawing.Point(0, 208);
+			this.skinnedPanel1.Margin = new System.Windows.Forms.Padding(0);
+			this.skinnedPanel1.Name = "skinnedPanel1";
+			this.skinnedPanel1.PanelType = Desktop.Skinning.SkinnedBackgroundType.PrimaryDark;
+			this.skinnedPanel1.Size = new System.Drawing.Size(657, 30);
+			this.skinnedPanel1.TabIndex = 14;
+			this.skinnedPanel1.TabSide = Desktop.Skinning.TabSide.None;
+			// 
+			// tabTroubleshoot
+			// 
+			this.tabTroubleshoot.BackColor = System.Drawing.Color.White;
+			this.tabTroubleshoot.Controls.Add(this.chkWorkflowTracer);
+			this.tabTroubleshoot.Controls.Add(this.chkAutoBackup);
+			this.tabTroubleshoot.ForeColor = System.Drawing.Color.Black;
+			this.tabTroubleshoot.Location = new System.Drawing.Point(104, 4);
+			this.tabTroubleshoot.Name = "tabTroubleshoot";
+			this.tabTroubleshoot.Padding = new System.Windows.Forms.Padding(3);
+			this.tabTroubleshoot.Size = new System.Drawing.Size(447, 173);
+			this.tabTroubleshoot.TabIndex = 4;
+			this.tabTroubleshoot.Text = "Diagnostics";
+			// 
+			// chkAutoBackup
+			// 
+			this.chkAutoBackup.AutoSize = true;
+			this.chkAutoBackup.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkAutoBackup.Location = new System.Drawing.Point(6, 6);
+			this.chkAutoBackup.Name = "chkAutoBackup";
+			this.chkAutoBackup.Size = new System.Drawing.Size(176, 17);
+			this.chkAutoBackup.TabIndex = 8;
+			this.chkAutoBackup.Text = "Create data recovery snapshots";
+			this.chkAutoBackup.UseVisualStyleBackColor = true;
+			// 
+			// chkWorkflowTracer
+			// 
+			this.chkWorkflowTracer.AutoSize = true;
+			this.chkWorkflowTracer.FieldType = Desktop.Skinning.SkinnedFieldType.Primary;
+			this.chkWorkflowTracer.Location = new System.Drawing.Point(6, 29);
+			this.chkWorkflowTracer.Name = "chkWorkflowTracer";
+			this.chkWorkflowTracer.Size = new System.Drawing.Size(245, 17);
+			this.chkWorkflowTracer.TabIndex = 9;
+			this.chkWorkflowTracer.Text = "Record workflow screenshots for crash reports";
+			this.chkWorkflowTracer.UseVisualStyleBackColor = true;
+			// 
 			// SettingsSetup
 			// 
 			this.AcceptButton = this.cmdOk;
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 			this.CancelButton = this.cmdCancel;
-			this.ClientSize = new System.Drawing.Size(625, 213);
+			this.ClientSize = new System.Drawing.Size(657, 238);
+			this.Controls.Add(this.skinnedPanel1);
+			this.Controls.Add(this.stripSections);
 			this.Controls.Add(this.tabsSections);
-			this.Controls.Add(this.cmdCancel);
-			this.Controls.Add(this.cmdOk);
+			this.MaximizeBox = false;
+			this.MinimizeBox = false;
 			this.Name = "SettingsSetup";
 			this.ShowIcon = false;
 			this.ShowInTaskbar = false;
+			this.Sizable = false;
 			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
 			this.Text = "Settings";
 			((System.ComponentModel.ISupportInitialize)(this.valAutoSave)).EndInit();
@@ -410,6 +628,11 @@
 			this.tabDialogue.PerformLayout();
 			this.tabBanter.ResumeLayout(false);
 			this.tabBanter.PerformLayout();
+			this.tabEpilogues.ResumeLayout(false);
+			this.tabEpilogues.PerformLayout();
+			this.skinnedPanel1.ResumeLayout(false);
+			this.tabTroubleshoot.ResumeLayout(false);
+			this.tabTroubleshoot.PerformLayout();
 			this.ResumeLayout(false);
 
 		}
@@ -417,34 +640,44 @@
 		#endregion
 
 		private System.Windows.Forms.FolderBrowserDialog folderBrowserDialog1;
-		private System.Windows.Forms.TextBox txtApplicationDirectory;
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Button cmdOk;
-		private System.Windows.Forms.Button cmdBrowse;
-		private System.Windows.Forms.Button cmdCancel;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.TextBox txtUserName;
+		private Desktop.Skinning.SkinnedTextBox txtApplicationDirectory;
+		private Desktop.Skinning.SkinnedLabel label1;
+		private Desktop.Skinning.SkinnedButton cmdOk;
+		private Desktop.Skinning.SkinnedButton cmdBrowse;
+		private Desktop.Skinning.SkinnedButton cmdCancel;
+		private Desktop.Skinning.SkinnedLabel label2;
+		private Desktop.Skinning.SkinnedTextBox txtUserName;
 		private System.Windows.Forms.OpenFileDialog openFileDialog1;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.NumericUpDown valAutoSave;
+		private Desktop.Skinning.SkinnedLabel label3;
+		private Desktop.Skinning.SkinnedNumericUpDown valAutoSave;
 		private System.Windows.Forms.ToolTip toolTip1;
-		private System.Windows.Forms.CheckBox chkIntellisense;
-		private System.Windows.Forms.CheckBox chkHideImages;
-		private System.Windows.Forms.Label label4;
-		private System.Windows.Forms.TextBox txtFilter;
-		private System.Windows.Forms.TabControl tabsSections;
+		private Desktop.Skinning.SkinnedCheckBox chkIntellisense;
+		private Desktop.Skinning.SkinnedCheckBox chkHidePrefixlessImages;
+		private Desktop.Skinning.SkinnedLabel label4;
+		private Desktop.Skinning.SkinnedTextBox txtFilter;
+		private Desktop.Skinning.SkinnedTabControl tabsSections;
 		private System.Windows.Forms.TabPage tabGeneral;
 		private System.Windows.Forms.TabPage tabDialogue;
-		private System.Windows.Forms.Button helpAutoSave;
-		private System.Windows.Forms.Button button1;
-		private System.Windows.Forms.Button helpIntellisense;
+		private Desktop.Skinning.SkinnedIcon helpAutoSave;
+		private Desktop.Skinning.SkinnedIcon button1;
+		private Desktop.Skinning.SkinnedIcon helpIntellisense;
 		private System.Windows.Forms.TabPage tabBanter;
-		private System.Windows.Forms.CheckBox chkAutoBanter;
-		private System.Windows.Forms.CheckBox chkAutoBackup;
-		private System.Windows.Forms.CheckBox chkInitialAdd;
-		private System.Windows.Forms.Button cmdBrowseKisekae;
-		private System.Windows.Forms.TextBox txtKisekae;
-		private System.Windows.Forms.Label label5;
-		private System.Windows.Forms.CheckBox chkDefaults;
+		private Desktop.Skinning.SkinnedCheckBox chkAutoBanter;
+		private Desktop.Skinning.SkinnedCheckBox chkInitialAdd;
+		private Desktop.Skinning.SkinnedButton cmdBrowseKisekae;
+		private Desktop.Skinning.SkinnedTextBox txtKisekae;
+		private Desktop.Skinning.SkinnedLabel label5;
+		private Desktop.Skinning.SkinnedCheckBox chkDefaults;
+		private System.Windows.Forms.TabPage tabEpilogues;
+		private Desktop.Skinning.SkinnedLabel label6;
+		private Desktop.Skinning.SkinnedCheckedListBox lstPauses;
+		private Desktop.Skinning.SkinnedCheckBox chkCaseTree;
+		private Desktop.Skinning.SkinnedCheckBox chkHideImages;
+		private Desktop.Skinning.SkinnedTabStrip stripSections;
+		private Desktop.Skinning.SkinnedPanel skinnedPanel1;
+		private Desktop.Skinning.SkinnedCheckBox chkColorTargets;
+		private System.Windows.Forms.TabPage tabTroubleshoot;
+		private Desktop.Skinning.SkinnedCheckBox chkWorkflowTracer;
+		private Desktop.Skinning.SkinnedCheckBox chkAutoBackup;
 	}
 }
\ No newline at end of file
diff --git a/editor source/SPNATI Character Editor/SettingsSetup.cs b/editor source/SPNATI Character Editor/SettingsSetup.cs
index 3419af2d7e411eff608689ae4925b1c4a57b3f1c..9f596640a5e9154e7b7f463fa17c9a5c24cf5921 100644
--- a/editor source/SPNATI Character Editor/SettingsSetup.cs	
+++ b/editor source/SPNATI Character Editor/SettingsSetup.cs	
@@ -1,12 +1,13 @@
 using Desktop;
+using Desktop.Skinning;
 using System;
-using System.Drawing;
+using System.Collections.Generic;
 using System.IO;
 using System.Windows.Forms;
 
 namespace SPNATI_Character_Editor
 {
-	public partial class SettingsSetup : Form
+	public partial class SettingsSetup : SkinnedForm
 	{
 		public SettingsSetup()
 		{
@@ -18,16 +19,39 @@ namespace SPNATI_Character_Editor
 			txtUserName.Text = Config.UserName;
 			valAutoSave.Value = Config.AutoSaveInterval;
 			chkIntellisense.Checked = Config.UseIntellisense;
-			chkHideImages.Checked = Config.UsePrefixlessImages;
+			chkHidePrefixlessImages.Checked = Config.UsePrefixlessImages;
 			txtFilter.Text = Config.PrefixFilter;
 			chkAutoBanter.Checked = Config.AutoLoadBanterWizard;
 			chkAutoBackup.Checked = Config.AutoBackupEnabled;
 			chkInitialAdd.Checked = Config.AutoOpenConditions;
 			chkDefaults.Checked = !Config.SuppressDefaults;
-		}
+			chkCaseTree.Checked = !Config.UseSimpleTree;
+			chkHideImages.Checked = Config.GetBoolean(Settings.HideImages);
+			chkColorTargets.Checked = Config.ColorTargetedLines;
+			chkWorkflowTracer.Checked = !Config.DisableWorkflowTracer;
 
+			HashSet<string> pauses = Config.AutoPauseDirectives;
+			foreach (DirectiveDefinition def in Definitions.Instance.Get<DirectiveDefinition>())
+			{
+				if (def.Key == "pause" || def.Key == "wait" || def.Key == "prompt")
+				{
+					continue;
+				}
+				lstPauses.Items.Add(def.Key, pauses.Contains(def.Key));
+			}
+			lstPauses.Sorted = true;
+		}
+		
 		private void cmdBrowse_Click(object sender, EventArgs e)
 		{
+			if (!string.IsNullOrEmpty(txtApplicationDirectory.Text))
+			{
+				try
+				{
+					folderBrowserDialog1.SelectedPath = txtApplicationDirectory.Text;
+				}
+				catch { }
+			}
 			if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
 			{
 				txtApplicationDirectory.Text = folderBrowserDialog1.SelectedPath;
@@ -77,19 +101,55 @@ namespace SPNATI_Character_Editor
 			Config.AutoSaveInterval = (int)valAutoSave.Value;
 			Config.UserName = txtUserName.Text;
 			Config.UseIntellisense = chkIntellisense.Checked;
-			Config.UsePrefixlessImages = chkHideImages.Checked;
+			Config.UsePrefixlessImages = chkHidePrefixlessImages.Checked;
 			Config.PrefixFilter = txtFilter.Text;
 			Config.AutoLoadBanterWizard = chkAutoBanter.Checked;
 			Config.AutoBackupEnabled = chkAutoBackup.Checked;
 			Config.AutoOpenConditions = chkInitialAdd.Checked;
-			Config.KisekaeDirectory = txtKisekae.Text;
+			if (txtKisekae.Text != Config.KisekaeDirectory)
+			{
+				if (!string.IsNullOrEmpty(Config.KisekaeDirectory))
+				{
+					CopyKisekaeImagesTo(txtKisekae.Text);
+				}
+				Config.KisekaeDirectory = txtKisekae.Text;
+			}
 			Config.SuppressDefaults = !chkDefaults.Checked;
+			Config.UseSimpleTree = !chkCaseTree.Checked;
+			Config.ColorTargetedLines = chkColorTargets.Checked;
+			Config.DisableWorkflowTracer = !chkWorkflowTracer.Checked;
+
+			HashSet<string> pauses = new HashSet<string>();
+			foreach (string item in lstPauses.CheckedItems)
+			{
+				pauses.Add(item);	
+			}
+			Config.AutoPauseDirectives = pauses;
+
 			DialogResult = DialogResult.OK;
 			Config.Save();
 			Shell.Instance.PostOffice.SendMessage(DesktopMessages.SettingsUpdated);
 			Close();
 		}
 
+		private void CopyKisekaeImagesTo(string newPath)
+		{
+			string oldDir = Path.Combine(Path.GetDirectoryName(Config.KisekaeDirectory), "images");
+			string newDir = Path.Combine(Path.GetDirectoryName(newPath), "images");
+			try
+			{
+				if (!Directory.Exists(newDir))
+				{
+					Directory.CreateDirectory(newDir);
+				}
+				foreach (string file in Directory.EnumerateFiles(oldDir))
+				{
+					File.Copy(file, Path.Combine(newDir, Path.GetFileName(file)));
+				}
+			}
+			catch { }
+		}
+
 		private void cmdCancel_Click(object sender, EventArgs e)
 		{
 			DialogResult = DialogResult.Cancel;
@@ -98,48 +158,16 @@ namespace SPNATI_Character_Editor
 
 		private void chkHideImages_CheckedChanged(object sender, EventArgs e)
 		{
-			txtFilter.Enabled = chkHideImages.Checked;
+			txtFilter.Enabled = chkHidePrefixlessImages.Checked;
 		}
 
-		private void tabsSections_DrawItem(object sender, DrawItemEventArgs e)
+		private void cmdBrowseKisekae_Click(object sender, EventArgs e)
 		{
-			Graphics g = e.Graphics;
-			Brush textBrush;
-			Brush backBrush;
-
-			// Get the item from the collection.
-			TabPage tabPage = tabsSections.TabPages[e.Index];
-
-			// Get the real bounds for the tab rectangle.
-			Rectangle tabBounds = tabsSections.GetTabRect(e.Index);
-
-			if (e.State == DrawItemState.Selected)
+			if (!string.IsNullOrEmpty(txtKisekae.Text))
 			{
-				// Draw a different background color, and don't paint a focus rectangle.
-				textBrush = new SolidBrush(Color.White);
-				backBrush = new SolidBrush(Color.SlateBlue);
-				g.FillRectangle(backBrush, e.Bounds);
+				openFileDialog1.InitialDirectory = Path.GetDirectoryName(txtKisekae.Text);
+				openFileDialog1.FileName = Path.GetFileName(txtKisekae.Text);
 			}
-			else
-			{
-				textBrush = new SolidBrush(e.ForeColor);
-				//e.DrawBackground();
-				g.FillRectangle(Brushes.White, e.Bounds);
-			}
-
-			// Use our own font.
-			Font tabFont = new Font("Arial", (float)11.0, FontStyle.Bold, GraphicsUnit.Pixel);
-
-			// Draw string. Center the text.
-			StringFormat stringFlags = new StringFormat();
-			stringFlags.Alignment = StringAlignment.Center;
-			stringFlags.LineAlignment = StringAlignment.Center;
-			g.DrawString(tabPage.Text, tabFont, textBrush, tabBounds, new StringFormat(stringFlags));
-		}
-
-		private void cmdBrowseKisekae_Click(object sender, EventArgs e)
-		{
-			openFileDialog1.FileName = txtKisekae.Text;
 			if (openFileDialog1.ShowDialog() == DialogResult.OK)
 			{
 				string file = openFileDialog1.FileName;
@@ -194,5 +222,11 @@ namespace SPNATI_Character_Editor
 				txtApplicationDirectory.Text = dir;
 			}
 		}
+
+		private void chkHideImages_CheckedChanged_1(object sender, EventArgs e)
+		{
+			Config.Set(Settings.HideImages, chkHideImages.Checked);
+			Shell.Instance.PostOffice.SendMessage(DesktopMessages.ToggleImages);
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/ShellLogic.cs b/editor source/SPNATI Character Editor/ShellLogic.cs
index eaf26fd1e923bb71764f054aa2cc3ff18b44586e..fc4d0ef72f189174e2f602af9895ee69154c8918 100644
--- a/editor source/SPNATI Character Editor/ShellLogic.cs	
+++ b/editor source/SPNATI Character Editor/ShellLogic.cs	
@@ -1,7 +1,12 @@
 using Desktop;
+using Desktop.Providers;
+using Desktop.Reporting;
+using Desktop.Skinning;
 using SPNATI_Character_Editor.Activities;
+using SPNATI_Character_Editor.DataSlicers;
 using SPNATI_Character_Editor.Forms;
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.Windows.Forms;
 using System.Windows.Threading;
@@ -20,6 +25,7 @@ namespace SPNATI_Character_Editor
 				return;
 			}
 			BuildDefinitions();
+			CreateActionBar();
 			CreateToolbar();
 
 			Shell.Instance.LaunchWorkspace(new LoaderRecord());
@@ -71,6 +77,8 @@ namespace SPNATI_Character_Editor
 		{
 			BuildDirectiveTypes();
 			BuildPropertyTypes();
+			BuildColorCodes();
+			BuildDataSlicers();
 		}
 
 		private static void BuildDirectiveTypes()
@@ -186,6 +194,20 @@ namespace SPNATI_Character_Editor
 			{
 				def.AllowedProperties.Add(key);
 			}
+
+			def = provider.Create("jump") as DirectiveDefinition;
+			def.Description = "Jumps to another scene.";
+			foreach (string key in new string[] { "id", "marker", "delay" })
+			{
+				def.AllowedProperties.Add(key);
+			}
+
+			def = provider.Create("prompt") as DirectiveDefinition;
+			def.Description = "Displays a multiple choice prompt to the user.";
+			foreach (string key in new string[] { "title", "marker", "delay" })
+			{
+				def.AllowedProperties.Add(key);
+			}
 		}
 
 		private static void BuildPropertyTypes()
@@ -220,6 +242,62 @@ namespace SPNATI_Character_Editor
 			Definitions.Instance.Add(property);
 		}
 
+		private static void BuildColorCodes()
+		{
+			Definitions.Instance.Add(new ColorCode("None", 0, skin => skin.Surface.ForeColor));
+			Definitions.Instance.Add(new ColorCode("Red", 1, skin => skin.Red));
+			Definitions.Instance.Add(new ColorCode("Orange", 2, skin => skin.Orange));
+			Definitions.Instance.Add(new ColorCode("Green", 3, skin => skin.Green));
+			Definitions.Instance.Add(new ColorCode("Blue", 4, skin => skin.Blue));
+			Definitions.Instance.Add(new ColorCode("Purple", 5, skin => skin.Purple));
+			Definitions.Instance.Add(new ColorCode("Pink", 6, skin => skin.Pink));
+			Definitions.Instance.Add(new ColorCode("Gray", 7, skin => skin.Gray));
+		}
+
+		private static void BuildDataSlicers()
+		{
+			SlicerProvider.AddSlicer("Case Type", "Groups by case type", () => new RecordSlicer(typeof(Trigger), "Tag", "Case Type", true, false));
+			SlicerProvider.AddSlicer("Target", "Groups by target", () => new RecordSlicer(typeof(Character), "Target", "Target", true, true));
+			SlicerProvider.AddSlicer("Target Hand", "Groups by self hand", () => new ComboSlicer(typeof(Case), "TargetHand", "Target Hand"));
+			SlicerProvider.AddSlicer("Target Stage", "Groups by target stage", () => new IntervalSlicer("TargetStage", "Target Stage", 0, 10));
+			SlicerProvider.AddSlicer("Target Tag", "Groups by target tag", () => new RecordSlicer(typeof(Tag), "Filter", "Tag", true, true));
+			SlicerProvider.AddSlicer("Target Time In Stage", "Groups by target time in stage", () => new IntervalSlicer("TargetTimeInStage", "Target Time In Stage", 0, 20));
+			SlicerProvider.AddSlicer("Target Layers", "Groups by target remaining layers", () => new IntervalSlicer("TargetLayers", "Target Layers", 0, 8));
+			SlicerProvider.AddSlicer("Target Starting Layers", "Groups by target remaining layers", () => new IntervalSlicer("TargetStartingLayers", "Target Starting Layers", 0, 8));
+			KeyValuePair<string, string>[] statuses = new KeyValuePair<string, string>[TargetCondition.StatusTypes.Length * 2];
+			for (int i = 0; i < TargetCondition.StatusTypes.Length; i++)
+			{
+				statuses[i * 2] = TargetCondition.StatusTypes[i];
+				statuses[i * 2 + 1] = new KeyValuePair<string, string>(TargetCondition.StatusTypes[i].Key == null ? null : "not_" + TargetCondition.StatusTypes[i].Key, "Not " + TargetCondition.StatusTypes[i].Value);
+			}
+			SlicerProvider.AddSlicer("Target Status", "Groups by target status", () => new ComboSlicer(typeof(Case), "TargetStatus", "Target Status", statuses));
+
+			SlicerProvider.AddSlicer("Also Playing", "Groups by Also Playing", () => new RecordSlicer(typeof(Character), "AlsoPlaying", "Also Playing", true, true));
+			SlicerProvider.AddSlicer("Also Playing Hand", "Groups by another character's hand", () => new ComboSlicer(typeof(Case), "AlsoPlayingHand", "Also Playing Hand"));
+			SlicerProvider.AddSlicer("Also Playing Stage", "Groups by another character's stage", () => new IntervalSlicer("AlsoPlayingStage", "Also Playing Stage", 0, 10));
+			SlicerProvider.AddSlicer("Also Playing Time In Stage", "Groups by another character's time in stage", () => new IntervalSlicer("AlsoPlayingTimeInStage", "Also Playing Time In Stage", 0, 20));
+
+			SlicerProvider.AddSlicer("Total Males", "Groups by total males", () => new IntervalSlicer("TotalMales", "Total Males", 0, 5));
+			SlicerProvider.AddSlicer("Total Females", "Groups by total females", () => new IntervalSlicer("TotalFemales", "Total Females", 0, 5));
+			SlicerProvider.AddSlicer("Total Playing", "Groups by total playing", () => new IntervalSlicer("TotalPlaying", "Total Playing", 0, 5));
+			SlicerProvider.AddSlicer("Total Exposed", "Groups by total exposed", () => new IntervalSlicer("TotalExposed", "Total Exposed", 0, 5));
+			SlicerProvider.AddSlicer("Total Naked", "Groups by total naked", () => new IntervalSlicer("TotalNaked", "Total Naked", 0, 5));
+			SlicerProvider.AddSlicer("Total Masturbating", "Groups by total masturbating", () => new IntervalSlicer("TotalMasturbating", "Total Masturbating", 0, 5));
+			SlicerProvider.AddSlicer("Total Finished", "Groups by total finished", () => new IntervalSlicer("TotalFinished", "Total Finished", 0, 5));
+			SlicerProvider.AddSlicer("Time In Stage", "Groups by own time in stage", () => new IntervalSlicer("TimeInStage", "Time In Stage", 0, 20));
+			SlicerProvider.AddSlicer("Total Rounds", "Groups by own total rounds completed", () => new IntervalSlicer("TotalRounds", "Total Rounds", 0, 20));
+			SlicerProvider.AddSlicer("Consecutive Losses", "Groups by losses in a row", () => new IntervalSlicer("ConsecutiveLosses", "Consecutive Losses", 0, 20));
+
+			SlicerProvider.AddSlicer("Hidden", "Groups by Hidden case", () => new BooleanSlicer("Hidden", "Hidden"));
+			SlicerProvider.AddSlicer("Said Marker", "Groups by marker", () => new RecordSlicer(typeof(Marker), "SaidMarker", "Said Marker", true, true));
+			SlicerProvider.AddSlicer("Not Said Marker", "Groups by marker not said", () => new RecordSlicer(typeof(Marker), "NotSaidMarker", "Not Said Marker", true, true));
+
+			SlicerProvider.AddSlicer("Play Once", "Groups by cases that play once per game", () => new OneShotSlicer());
+			SlicerProvider.AddSlicer("Has Hand", "Groups by self hand", () => new ComboSlicer(typeof(Case), "HasHand", "Own Hand"));
+
+			SlicerProvider.AddSlicer("Case Stage", "Groups by what stage the case applies to", () => new StageSlicer());
+		}
+
 		private static bool DoInitialSetup()
 		{
 			string appDir = Config.GetString(Settings.GameDirectory);
@@ -229,7 +307,7 @@ namespace SPNATI_Character_Editor
 			}
 			if (string.IsNullOrEmpty(Config.GetString(Settings.GameDirectory)))
 			{
-				if (OpenSetup() == DialogResult.Cancel)
+				if (OpenInitialSetup() == DialogResult.Cancel)
 				{
 					ErrorLog.LogError("Unable to launch because setup was cancelled.");
 					return false;
@@ -250,12 +328,47 @@ namespace SPNATI_Character_Editor
 		}
 
 		/// <summary>
-		/// Opens the initial setup form
+		/// Opens the first time settings screen
 		/// </summary>
-		private static DialogResult OpenSetup()
+		/// <returns></returns>
+		private static DialogResult OpenInitialSetup()
 		{
-			SettingsSetup form = new SettingsSetup();
-			return form.ShowDialog();
+			FirstLaunchSetup setup = new FirstLaunchSetup();
+			return setup.ShowDialog();
+		}
+
+		private static void CreateActionBar()
+		{
+			Shell shell = Shell.Instance;
+
+			shell.AddActionItem(Properties.Resources.Settings, "Open Setup", "Open Setup", Setup, null);
+
+			ToolStripMenuItem themes = shell.AddActionMenu(Properties.Resources.Theme, "Change Theme");
+
+			List<Skin> darkThemes = new List<Skin>();
+
+			foreach (Skin skin in SkinManager.Instance.AvailableSkins)
+			{
+				if (skin.Group == "Dark")
+				{
+					darkThemes.Add(skin); //add dark themes after light ones
+				}
+				else
+				{
+					shell.AddActionItem(skin.GetIcon(), skin.Name, skin.Description, () => ChangeTheme(skin), themes);
+				}
+			}
+			shell.AddActionSeparator(themes);
+			foreach (Skin skin in darkThemes)
+			{
+				shell.AddActionItem(skin.GetIcon(), skin.Name, skin.Description, () => ChangeTheme(skin), themes);
+			}
+		}
+
+		private static void ChangeTheme(Skin skin)
+		{
+			SkinManager.Instance.SetSkin(skin);
+			Config.Skin = skin.Name;
 		}
 
 		private static void CreateToolbar()
@@ -270,7 +383,7 @@ namespace SPNATI_Character_Editor
 			shell.AddToolbarItem("Import .txt...", ImportCharacter, menu, Keys.Control | Keys.I);
 			shell.AddToolbarItem("Export .txt for make_xml.py", ExportCharacter, menu, Keys.Control | Keys.E);
 			shell.AddToolbarSeparator(menu);
-			shell.AddToolbarItem("Setup...", Setup, menu, Keys.None);
+			shell.AddToolbarItem("Settings...", Setup, menu, Keys.None);
 			shell.AddToolbarSeparator(menu);
 			shell.AddToolbarItem("Exit", Exit, menu, Keys.Alt | Keys.F4);
 
@@ -282,7 +395,7 @@ namespace SPNATI_Character_Editor
 
 			//Characters
 			shell.AddToolbarItem("Characters...", OpenCharacterSelect, Keys.None);
-			shell.AddToolbarItem("Skins...", typeof(Costume));
+			shell.AddToolbarItem("Skins...", OpenCostumeSelect, Keys.None);
 
 			//Validate
 			menu = shell.AddToolbarSubmenu("Validate");
@@ -297,10 +410,18 @@ namespace SPNATI_Character_Editor
 			shell.AddToolbarItem("Configure Game...", ConfigGame, menu, Keys.None);
 			shell.AddToolbarItem("Manage Macros...", ManageCaseMacros, menu, Keys.None);
 			shell.AddToolbarItem("Manage Dictionary...", typeof(DictionaryRecord), menu);
+			shell.AddToolbarItem("Manage Recipes...", typeof(Recipe), GetRecipe, menu);
 			shell.AddToolbarSeparator(menu);
 			shell.AddToolbarItem("Data Recovery", OpenDataRecovery, menu, Keys.None);
 			shell.AddToolbarItem("Fix Kisekae", ResetKisekae, menu, Keys.None);
 
+			//Dev tools
+			if (Config.DevMode)
+			{
+				menu = shell.AddToolbarSubmenu("Dev");
+				shell.AddToolbarItem("Themes...", typeof(Skin), menu);
+			}
+
 			//Help
 			shell.AddToolbarSeparator();
 			menu = shell.AddToolbarSubmenu("Help");
@@ -329,6 +450,15 @@ namespace SPNATI_Character_Editor
 			}
 		}
 
+		private static void OpenCostumeSelect()
+		{
+			IRecord record = RecordLookup.DoLookup(typeof(Costume), "", true, CharacterDatabase.FilterDefaultCostume, null);
+			if (record != null)
+			{
+				Shell.Instance.LaunchWorkspace(record as Costume);
+			}
+		}
+
 		private static void Save()
 		{
 			Save(false, Shell.Instance.ActiveWorkspace);
@@ -447,7 +577,7 @@ namespace SPNATI_Character_Editor
 				}
 			}
 			string dir = Config.GetRootDirectory(current);
-			string file = Shell.Instance.ShowOpenFileDialog(dir, "edit-dialogue.txt");
+			string file = Shell.Instance.ShowOpenFileDialog(dir, "edit-dialogue.txt", "Text files|*.txt|All files|*.*");
 			if (!string.IsNullOrEmpty(file))
 			{
 				FlatFileSerializer.Import(file, current);
@@ -504,5 +634,17 @@ namespace SPNATI_Character_Editor
 			form.ShowDialog();
 			Shell.Instance.PostOffice.SendMessage(DesktopMessages.MacrosUpdated);
 		}
+
+		private static IRecord GetRecipe()
+		{
+			IRecord record = RecordLookup.DoLookup(typeof(Recipe), "", true, FilterCoreRecipes, true, null);
+			return record;
+		}
+
+		private static bool FilterCoreRecipes(IRecord record)
+		{
+			Recipe recipe = record as Recipe;
+			return Config.DevMode || !recipe.Core;
+		}
 	}
 }
diff --git a/editor source/SPNATI Character Editor/VersionHistory/v5.0.html b/editor source/SPNATI Character Editor/VersionHistory/v5.0.html
new file mode 100644
index 0000000000000000000000000000000000000000..9a9126b408775341ff034ce713deaec6597cdfb8
--- /dev/null
+++ b/editor source/SPNATI Character Editor/VersionHistory/v5.0.html	
@@ -0,0 +1,198 @@
+<h3>Themes</h3>
+<ul>
+    <li>The editor has received a complete UI overhaul, bringing in theme support.</li>
+    <li>Use the menu in the upper right of the window to change your theme.</li>
+    <li>By popular demand, included are a few Dark Mode variants.</li>
+</ul>
+
+<h3>General</h3>
+<ul>
+    <li>"throw" intelligence added. This type of AI actively attempts to lose.</li>
+    <li>Setting added to hide images in the sidebar to allow for less conspicuous line writing. (Setup menu > General)</li>
+    <li>The setup menu is now accessible from the new action toolbar in the upper right of the main window via the Gear button.</li>
+    <li>Character selection now displays how long since a character was updated as well as their offline/incomplete status.</li>
+</ul>
+
+<h3>Tags</h3>
+<ul>
+    <li>Hovering over a tag's name will now show a more detailed description about what the tag means.</li>
+</ul>
+
+<h3>Quick Reference</h3>
+<ul>
+    <li>A quick reference has been added to the sidebar, accessible via the Reference button. This area contains helpful information at a glance while writing lines.</li>
+    <li>Initially this includes all tags and their definitions, but more types of reference material may be added in the future.</li>
+</ul>
+
+<h3>Dialogue</h3>
+<ul>
+    <li>
+        Added the ability to specify different poses for each stage for a line of dialogue within a case. For example, if you want to say "Hi!" in stages 0-5 with pose "happy" and "Hi!" in stage 6 with pose "mad", you can now use a single case and give stage 6 an override pose by clicking the double arrow next to the pose selection dropdown.
+        As a result, <strong style="color:red">upon saving you may see a few lines shuffle around within a case, and duplicate lines with different poses within the same case will be deleted.</strong> If this change in behavior with not allowing duplicate lines in the same case really bothers you, complain on Discord.
+    </li>
+    <li>The Case Tree has received a face lift in an effort to better organize cases. If you don't like the column layout, you can switch back to the old style in Setup > Dialogue. Green coloring for targeting lines was also removed, but you can re-enable it in the Setup.</li>
+    <li>Items in the case tree will update immediately now when conditions change. The trade off is somewhat slower saves.</li>
+    <li>For cases that span multiple stages, you can now choose which stage the preview image uses instead of always using the earliest stage. Right-click a stage's checkbox to activate it as the preview. This setting does not persist between cases.</li>
+    <li>Added a speed button for a ~player~ variable test (Game > Player Name).</li>
+    <li>Added conditions for testing what place a character is in based on who has the most layers remaining, and for comparing the layer difference between two players.</li>
+    <li>A Player speed menu has been added containing conditions for checking information about any particular player. These conditions take a player, which can be either "self", the case's target, or a specific character.</li>
+    <li>Cases can now be assigned a custom label and/or color to appear in the case tree for that case, making it easier to tell at a glance which case is what.</li>
+    <li>Respond to This now opens in a tab instead of a popup. Hopefully this avoids the issue with Mac where the popup would get stuck open.</li>
+    <li>The marker button in the dialogue grid now has a tooltip showing the marker's value if there is one.</li>
+    <li>Tweaked the sorting order of cases to better group similar cases.</li>
+    <li>Variable tests on ~clothing~ now provide an autocomplete list of all clothing in the game so you know whether anyone actually uses that clothing.</li>
+    <li>A "Sort by Priority" button has been added to the case tree, which will force a resort on all cases that have been added since last time.</li>
+    <li>Added options to play a case or individual line from a case once per game. For entire cases, use Self > Play Once. For individual lines, the option is under the line's ... menu. </li>
+    <li>Removed the hard error about unrecognized variables in dialogue. Unrecognized variables will still appear during validation, but you won't be preventing from saving a line with one.</li>
+    <li>You can now provide multiple sets of conditions for the same case. The case will play if any set of conditions is met. Previously to play a set of lines when either condition A is met or condition B is met, you would need to duplicate the case. Now, click +OR in the conditions area of a case to add a new condition set. Each set will get is own tab.</li>
+</ul>
+
+<h3>Macros</h3>
+<ul>
+    <li>Macros can now be added to a specific speed Menu rather than just the Macros menu. They will appear alphabetized at the bottom of that menu.</li>
+    <li>Use Tools > Manage Macros to assign menus to your existing macros.</li>
+    <li>The Macro Editor now has all the same conditions as the main dialogue editor. Previously shortcuts for variable tests (ex. Costume) were not included.</li>
+</ul>
+
+<h3>Recipes</h3>
+<ul>
+    <li>Get your dialogue cooking by using recipes!</li>
+    <li>Use the flask in the Case Tree to quickly create a case for interesting game situations with the appropriate conditions already filled in.</li>
+    <li>Example recipes include: Losing the first hand, first instance of nudity in the game, final hand of the game, and more.</li>
+    <li>Create your own recipes using Tools > Manage Recipes. Recipes are stored in %appdata%/SPNATI/Recipes. You can share your own recipes with other creators who merely have to plug your recipe files into that folder for them to become available. Or, if you've cooked up something really special, consider submitting it to become a default recipe in the editor.</li>
+</ul>
+
+<h3>Advanced Metadata</h3>
+<ul>
+    <li>Added "Z Layer" and "Speech bubble position" options for customizing character layering along with speech bubble layering relative to the pose.</li>
+    <li>Added nickname support. A nickname is what your character will call another character when using ~name~ instead of their actual name.</li>
+    <li>Added support for custom text formatting. This lets you add effects to text (colors, underlines, strikethrough, and so on). Please be judicious in usage and not make things fancy just to look cool.</li>
+</ul>
+
+<h3>Line Importer</h3>
+<ul>
+    <li>A "Line Importer" tab has been added to the character workspace for importing lines created in the game's dev mode.</li>
+</ul>
+
+<h3>Collectibles</h3>
+<ul>
+    <li>Collectibles are no longer sorted by ID. Use the up and down arrows to sort a character's collectibles to your tastes.</li>
+</ul>
+
+<h3>Epilogue Editor</h3>
+<ul>
+    <li>The setup menu now contains an Epilogues tab where you can configure which directive types to automatically add a "Wait for Input" directive after. For instance, you can make it automatically add a user pause whenever you add a speech bubble.</li>
+</ul>
+
+<h3>Markers</h3>
+<ul>
+    <li>Added a Delete button to each row to be consistent with the dialogue table.</li>
+</ul>
+
+<h3>Situations</h3>
+<ul>
+    <li>Added a Delete button to each row to be consistent with the dialogue table.</li>
+</ul>
+
+<h3>Pose Maker</h3>
+<ul>
+    <li>Added a "Reload Assets" button to immediately load sprites from disk so that changes to image files are reflected in the editor without requiring a restart.</li>
+    <li>Added a button to the sprite header for changing its initial image in order to make changing out images a little faster.</li>
+    <li>Double-clicking a sprite's header will now select its first keyframe for quicker access to animated properties.</li>
+    <li>Added a Name field to the Create Sequence wizard.</li>
+    <li>Added an "Export to GIF" button to make it easier to post poses on Discord for feedback. Note that you'll likely get better compression using a dedicated program like ScreenToGif.</li>
+    <li>Clicking Create Sequence on a previously created sequence will now let you add frames or change the delay between frames.</li>
+</ul>
+
+<h3>Pose Lists</h3>
+<ul>
+    <li>Selecting a row in a pose list will now show the image, if available, in the main sidebar preview.</li>
+    <li>Added a "Copy Cropping Info" button for quickly copying the L,R,T,B values from one pose to one or more others in the same list.</li>
+    <li>Added an "Open Folder" button for opening the character's folder in Explorer.</li>
+    <li>Screenshots tab is now available in alternate skin workspaces</li>
+</ul>
+
+<h3>Pose Usage</h3>
+<ul>
+    <li>New chart in the character workspace for getting a high-level overview of the frequency of usage of each pose name broken down by stage.</li>
+</ul>
+
+<h3>Line Slicer</h3>
+<ul>
+    <li>New reporting tool in the character workspace for getting a high-level overview of conditions used with your character's lines. See the Help files for more details.</li>
+</ul>
+
+<h3>Banter Wizard</h3>
+<ul>
+    <li>The Banter Wizard now offers the same options as the Writing Aid when writing a line. You can accept the response, discard it, or open it in the main dialogue editor.</li>
+    <li>When creating a new response, the full case editor is also now available in the Banter Wizard rather than just the lines portion, allowing you to tweak the conditions of the case as needed.</li>
+</ul>
+
+<h3>Character-Specific Editor Configuration</h3>
+<ul>
+    <li>Added a Configuration tab to the character workspace for configuring the editor per-character rather than per-user.</li>
+    <li>You can now specify multiple image prefixes to exclude from pose image dropdowns for a particular character. This applies in addition to the global prefix filter found in the Setup form.</li>
+    <li>Unlike the global setting, character-specific filters will apply for anyone who opens the character in their editor.</li>
+</ul>
+
+<h3>Validation</h3>
+<ul>
+    <li>Added collectible validation for broken images and unused collectibles.</li>
+    <li>Added validation for Saying Text conditions where the target never says that text.</li>
+    <li>Removed validation warning when targeting offline or incomplete opponents.</li>
+</ul>
+
+<h3>Charts</h3>
+<ul>
+    <li>Added a Clothing Frequency chart which lists out the names of clothing most commonly used across characters. This is helpful for knowing what types of clothing to target in a variable test.</li>
+</ul>
+
+<h3>Help Files</h3>
+<ul>
+    <li>The help files have been finally brought up to date.</li>
+</ul>
+
+<h3>Crash Reports</h3>
+<ul>
+    <li>In the event of a crash, the editor will now create a .ZIP file containing the crash details along with screenshots of the last ten clicks that occurred within the program. This ZIP can aid in troubleshooting the source of a crash so that it can be fixed more quickly. These screenshots only record the Character Editor window and nothing else from your desktop. Regardless, if you do not wish for screenshots to be recorded, you can disable this feature from the Settings screen.</li>
+</ul>
+
+<h3>Bug Fixes</h3>
+<ul>
+    <li>Respond to This popup can now be resized.</li>
+    <li>Copying a sprite across poses will no longer append "(copy)" the ID if it's already unique.</li>
+    <li>Pasting or duplicating a sprite now auto-selects the new sprite.</li>
+    <li>Fixed issue where using variables in dialogue that come from a Filter (+) condition would complain about the variable being invalid.</li>
+    <li>Variables defined in a Filter (+) condition now appear in Intellisense.</li>
+    <li>Fixed issue where using the ID "fade" or "camera" in a "stop" epilogue directive would get you stuck in the ID field.</li>
+    <li>Fixed issue where the pose preview wouldn't update when clicking a new case.</li>
+    <li>Fixed issue with eyelashes not importing from Kisekae.</li>
+    <li>Fixed the issue with the shift key getting stuck on by removing the ability to delete dialogue lines by clicking the row header and pressing Delete.</li>
+    <li>Fixed crash when closing Mavis' workspace after entering the Pose Maker.</li>
+    <li>Speech arrow locations are no longer restricted to 0-100%</li>
+    <li>Unrecognized elements in meta.xml and collectibles.xml will now be preserved, to help make new features less reliant on an up-to-date editor.</li>
+    <li>Fixed issue where filters weren't determining equivalence properly, causing cases to inappropriately merge.</li>
+    <li>Fixed help text for the collectible Counter field.</li>
+    <li>Fixed issue where images used by collectibles were reported in the validator as unused.</li>
+    <li>Leading or trailing whitespace is now trimmed from markers.</li>
+    <li>Sprites are now automatically selected when added in the pose maker.</li>
+    <li>Fixed an issue where find/replace would stop working in stage view after a few results.</li>
+    <li>~ is now auto-stripped from filter variables to reduce confusion about whether they're supposed to have them or not.</li>
+    <li>Fixed issue where two cases with the same conditions but different priorities would merge when saved.</li>
+    <li>Fixed crash when clearing the Base Height field of a Pose Marker pose.</li>
+    <li>Removed dead link about the long-retired Dialogue Tester in the help files.</li>
+    <li>Fixed issue where recent females would appear when targeting a male case and vice versa.</li>
+    <li>Fixed issues where punctuation typos with variables like <pre style="display:inline-block">~player!~</pre> would not be flagged as invalid. </li>
+    <li>Fixed issue where saving while focus is in a cell on the wardrobe editor would not save that cell's new value.</li>
+    <li>Fixed crash when clearing out a Pose Maker pose's ID field.</li>
+    <li>Fixed crash when deleting an ending.</li>
+    <li>Fixed crash when accessing the Characters menu after using a macro with an empty Target or Also Playing field.</li>
+    <li>Fixed issue where responding to a case that uses Not Said Marker with lines that set the same marker would produce a response that is impossible to trigger.</li>
+    <li>Fixed issue where responding to a "Must masturbate, first to do so", wouldn't guarantee that the response triggers only for the first to do so.</li>
+    <li>Fixed issue where responding to a chest or crotch reveal on an opponent that has no important items would not create a response of the right case type.</li>
+    <li>Importing all poses where one or more use image attachments will now ask for all the unrecognized attachments up front to avoid a crash.</li>
+    <li>Columns that are too wide to fit in the search lookup (e.g. recipes) now have tooltips to show the full text.</li>
+    <li>Fixed issue where adding a case for a post-naked phase on a character with fewer than 8 layers would adding the case to the wrong stage (ex. finished would go to the masturbating stage, masturbating would go to the naked stage, etc.)</li>
+    <li>Fixed issue where creating a response for a case you already created a response to would create a new case.</li>
+    <li>Fixed issue where responding to a Game Over (Defeat) case that is targeting your character (meaning your character was the winner) would make a Game Over (Defeat) case instead of a Game Over (Victory).</li>
+</ul>
diff --git a/editor source/SPNATI Character Editor/WorkflowFilter.cs b/editor source/SPNATI Character Editor/WorkflowFilter.cs
new file mode 100644
index 0000000000000000000000000000000000000000..266393f6f50dbd7fb2471bb27f0ea7bee4371f69
--- /dev/null
+++ b/editor source/SPNATI Character Editor/WorkflowFilter.cs	
@@ -0,0 +1,96 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace SPNATI_Character_Editor
+{
+	public class WorkflowTracker : IMessageFilter
+	{
+		private const int TrackedScreens = 10;
+
+		private LinkedList<Bitmap> _screens = new LinkedList<Bitmap>();
+
+		public const int WM_LBUTTONDOWN = 0x0201;
+		public enum enmScreenCaptureMode
+		{
+			Screen,
+			Window
+		}
+
+		[DllImport("user32.dll")]
+		private static extern IntPtr GetForegroundWindow();
+
+		[DllImport("user32.dll")]
+		private static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
+
+		[StructLayout(LayoutKind.Sequential)]
+		private struct Rect
+		{
+			public int Left;
+			public int Top;
+			public int Right;
+			public int Bottom;
+		}
+
+		public Bitmap Capture(enmScreenCaptureMode screenCaptureMode = enmScreenCaptureMode.Window)
+		{
+			Rectangle bounds;
+
+			if (screenCaptureMode == enmScreenCaptureMode.Screen)
+			{
+				bounds = System.Windows.Forms.Screen.GetBounds(Point.Empty);
+				CursorPosition = Cursor.Position;
+			}
+			else
+			{
+				var foregroundWindowsHandle = GetForegroundWindow();
+				var rect = new Rect();
+				GetWindowRect(foregroundWindowsHandle, ref rect);
+				bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
+				CursorPosition = new Point(Cursor.Position.X - rect.Left, Cursor.Position.Y - rect.Top);
+			}
+
+			var result = new Bitmap(bounds.Width, bounds.Height);
+
+			using (var g = Graphics.FromImage(result))
+			{
+				g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
+				g.FillEllipse(Brushes.Red, Cursor.Position.X - 2 - bounds.Left, Cursor.Position.Y - 2 - bounds.Top, 5, 5);
+			}
+
+			return result;
+		}
+
+		public Point CursorPosition
+		{
+			get;
+			protected set;
+		}
+
+		public bool PreFilterMessage(ref Message m)
+		{
+			if (m.Msg == WM_LBUTTONDOWN)
+			{
+				if (!Config.DisableWorkflowTracer)
+				{
+					_screens.AddLast(Capture(enmScreenCaptureMode.Window));
+					if (_screens.Count > TrackedScreens)
+					{
+						_screens.RemoveFirst();
+					}
+				}
+			}
+			return false;
+		}
+
+		public IEnumerable<Bitmap> GetScreens()
+		{
+			return _screens;
+		}
+	}
+}
diff --git a/editor source/SPNATI Character Editor/WorkspaceMessages.cs b/editor source/SPNATI Character Editor/WorkspaceMessages.cs
index f4c6b9670a8f56ffaf668c414169a3bf6f2916a6..99ea7a1d8b60bbd542f0e3b4970a200a6ee64400 100644
--- a/editor source/SPNATI Character Editor/WorkspaceMessages.cs	
+++ b/editor source/SPNATI Character Editor/WorkspaceMessages.cs	
@@ -36,5 +36,10 @@
 		/// Sent to inform the preview sidebar to update its text [DialogueLine: line information to display]
 		/// </summary>
 		public const int PreviewLine = 7;
+
+		/// <summary>
+		/// Sent to inform the preview sidebar to update its image [List&lt;string&gt;: markers that are set]
+		/// </summary>
+		public const int UpdateMarkers = 8;
 	}
 }
diff --git a/editor source/SPNATI Character Editor/XMLHelper.cs b/editor source/SPNATI Character Editor/XMLHelper.cs
index b0c20b7079a91cf6da80ebf1f1d0555c6861f4c0..fd7a4d90a203a59c4192111d668fb5aae6b52563 100644
--- a/editor source/SPNATI Character Editor/XMLHelper.cs	
+++ b/editor source/SPNATI Character Editor/XMLHelper.cs	
@@ -61,6 +61,14 @@ namespace SPNATI_Character_Editor
 			text = text.Replace("<I>", "&lt;i&gt;");
 			text = text.Replace("</i>", "&lt;/i&gt;");
 			text = text.Replace("</I>", "&lt;/i&gt;");
+			text = text.Replace("<br>", "&lt;br&gt;");
+			text = text.Replace("<BR>", "&lt;BR&gt;");
+			text = text.Replace("</br>", "&lt;/br&gt;");
+			text = text.Replace("</BR>", "&lt;/BR&gt;");
+			text = text.Replace("<hr>", "&lt;hr&gt;");
+			text = text.Replace("<HR>", "&lt;HR&gt;");
+			text = text.Replace("</hr>", "&lt;/hr&gt;");
+			text = text.Replace("</HR>", "&lt;/HR&gt;");
 			return text;
 		}
 
diff --git a/editor source/SPNATI Character Editor/tag_dictionary.xml b/editor source/SPNATI Character Editor/tag_dictionary.xml
index 27c928d2e5172dbfb49e3013675834f138c350ab..701a209271cb9e5bb7f2b1a3f85f49ae60705bce 100644
--- a/editor source/SPNATI Character Editor/tag_dictionary.xml	
+++ b/editor source/SPNATI Character Editor/tag_dictionary.xml	
@@ -1,428 +1,432 @@
 <?xml version="1.0" encoding="utf-8"?>
 <tagdictionary>
   <group label="Hair Color">
-    <tag display="Black" description="Black/dark gray hair">black_hair</tag>
-    <tag display="Blonde" description="Yellow/blonde hair">blonde</tag>
-    <tag display="Brown">brunette</tag>
-    <tag display="Red/Orange">ginger</tag>
-    <tag display="White/Gray">white_hair</tag>
-    <tag display="Blue" paired="exotic_hair">blue_hair</tag>
-    <tag display="Pink" paired="exotic_hair">pink_hair</tag>
-    <tag display="Green" paired="exotic_hair">green_hair</tag>
-    <tag display="Purple" paired="exotic_hair">purple_hair</tag>
-    <tag display="Exotic">exotic_hair</tag>
+    <tag display="Black" description="The character has dark hair">black_hair</tag>
+    <tag display="Blonde" description="The character has yellow or blonde hair">blonde</tag>
+    <tag display="Brown" description="The character has brown hair">brunette</tag>
+    <tag display="Red/Orange" description="The character has red or orange hair, whether natural or dyed">ginger</tag>
+    <tag display="White/Gray" description="The character has white, gray, or silver hair">white_hair</tag>
+    <tag display="Blue" paired="exotic_hair" description="The character has blue, aqua, or cyan hair">blue_hair</tag>
+    <tag display="Pink" paired="exotic_hair" description="The character has pink hair">pink_hair</tag>
+    <tag display="Green" paired="exotic_hair" description="The character has green hair">green_hair</tag>
+    <tag display="Purple" paired="exotic_hair" description="The character has purple hair">purple_hair</tag>
+    <tag display="Exotic" description="The character has multi-colored hair or a non-natural color">exotic_hair</tag>
   </group>
   <group label="Hair Length">
-    <tag display="Bald">bald</tag>
-    <tag display="Short">short_hair</tag>
-    <tag display="Medium">medium_hair</tag>
-    <tag display="Long">long_hair</tag>
-    <tag display="Very Long" paired="long_hair">very_long_hair</tag>
+    <tag display="Bald" description="Little or no hair on the head">bald</tag>
+    <tag display="Short" description="Hair does not pass the jawline">short_hair</tag>
+    <tag display="Medium" description="Hair goes below the jaw and may reach the shoulders">medium_hair</tag>
+    <tag display="Long" description="Hair goes beyond the shoulders">long_hair</tag>
+    <tag display="Very Long" paired="long_hair" description="Hair reaches the thighs or beyond">very_long_hair</tag>
   </group>
   <group label="Hair Style" multiselect="true">
-    <tag display="Antenna/Ahoge">ahoge</tag>
-    <tag display="Drills">drill_hair</tag>
-    <tag display="Messy">messy_hair</tag>
-    <tag display="Ponytail">ponytail</tag>
-    <tag display="Twintails">twintails</tag>
+    <tag display="Antenna/Ahoge" description="The character has antenna-like hair poking out">ahoge</tag>
+    <tag display="Drills" description="Anime-style drill hair">drill_hair</tag>
+    <tag display="Messy" description="Hair is notably untidy or wild">messy_hair</tag>
+    <tag display="Ponytail" description="Hair is up in a ponytail">ponytail</tag>
+    <tag display="Twintails" description="Hair is fashioned into two tails anywhere on the head">twintails</tag>
   </group>
   <group label="Eye Color">
-    <tag display="Dark/Black/Brown">dark_eyes</tag>
-    <tag display="Blue">blue_eyes</tag>
-    <tag display="Pale/White/Gray">pale_eyes</tag>
-    <tag display="Green">green_eyes</tag>
-    <tag display="Pink/Purple">violet_eyes</tag>
-    <tag display="Red">red_eyes</tag>
-    <tag display="Amber/Orange/Yellow">amber_eyes</tag>
-	<tag display="Differently-colored">heterochromia</tag>
+    <tag display="Dark/Black/Brown" description="Brown, black, or dark gray eyes">dark_eyes</tag>
+    <tag display="Blue" description="Blue eyes">blue_eyes</tag>
+    <tag display="Pale/White/Gray" description="White or medium-light eyes">pale_eyes</tag>
+    <tag display="Green" description="Green eyes">green_eyes</tag>
+    <tag display="Pink/Purple" description="Purple or pink eyes or somewhere in between">violet_eyes</tag>
+    <tag display="Red" description="Red eyes">red_eyes</tag>
+    <tag display="Amber/Orange/Yellow" description="Yellow or orange eyes">amber_eyes</tag>
+    <tag display="Differently-colored" description="Eyes are different colors">heterochromia</tag>
   </group>
   <group label="Skin Tone">
-    <tag display="Brown">dark-skinned</tag>
-    <tag display="Fair">fair-skinned</tag>
-    <tag display="Olive">olive-skinned</tag>	
-    <tag display="Pale">pale-skinned</tag>
-    <tag display="Unusual">unusual_skin</tag>
+    <tag display="Brown" description="Notably dark skin">dark-skinned</tag>
+    <tag display="Fair" description="Light skin suggesting Caucasian or East-Asian ethnicity">fair-skinned</tag>
+    <tag display="Olive" description="Between fair-skinned and dark-skinned">olive-skinned</tag>
+    <tag display="Pale" description="Notably pale skin">pale-skinned</tag>
+    <tag display="Unusual" description="Unnatural skin tone">unusual_skin</tag>
   </group>
   <group label="Physical Build" multiselect="true">
-    <tag display="Athletic">athletic</tag>
-    <tag display="Muscular" paired="athletic">muscular</tag>
-    <tag display="Fat/Chubby">chubby</tag>
-    <tag display="Short">short</tag>
-    <tag display="Tall">tall</tag>
-    <tag display="Androgynous">androgynous</tag>
-	  <tag display="Big Butt">big_butt</tag>
+    <tag display="Athletic" description="Traditionally athletic, thin but defined muscles">athletic</tag>
+    <tag display="Muscular" paired="athletic" description="Built with overt musculature and distinct abs">muscular</tag>
+    <tag display="Fat/Chubby" description="Has pronounced body fat. Anywhere from a little extra, or beer belly, or larger">chubby</tag>
+    <tag display="Short" description="Character is notably shorter than average">short</tag>
+    <tag display="Tall" description="Character is notably taller than average">tall</tag>
+    <tag display="Androgynous" description="Character could pass for either gender">androgynous</tag>
+    <tag display="Big Butt" description="Notably large derrière">big_butt</tag>
   </group>
   <group label="Pubic Hair">
-    <tag display="Shaved">shaved</tag>
-    <tag display="Trimmed" paired="pubic_hair">trimmed</tag>
-    <tag display="Hairy" paired="pubic_hair">hairy</tag>
-    <tag display="Any pubic hair">pubic_hair</tag>
+    <tag display="Shaved" description="No pubic hair">shaved</tag>
+    <tag display="Trimmed" paired="pubic_hair" description="Pubic hair is sparse or neatly trimmed">trimmed</tag>
+    <tag display="Hairy" paired="pubic_hair" description="Pubic hair is thick, dense, and/or unmaintained">hairy</tag>
+    <tag display="Any pubic hair" description="Character has any amount of pubic hair">pubic_hair</tag>
   </group>
   <group label="Breast Size" gender="female">
-    <tag display="Small">small_breasts</tag>
-    <tag display="Medium">medium_breasts</tag>
-    <tag display="Large">large_breasts</tag>
-    <tag display="Huge" paired="large_breasts">huge_breasts</tag>
+    <tag display="Small" description="Breasts are smaller than average">small_breasts</tag>
+    <tag display="Medium" description="Breasts are average">medium_breasts</tag>
+    <tag display="Large" description="Breasts are larger than average">large_breasts</tag>
+    <tag display="Huge" paired="large_breasts" description="Breasts are extremely large">huge_breasts</tag>
   </group>
   <group label="Penis Size" gender="male">
-    <tag display="Small">small_penis</tag>
-    <tag display="Medium">medium_penis</tag>
-    <tag display="Large">large_penis</tag>
-    <tag display="Huge" paired="large_penis">huge_penis</tag>
+    <tag display="Small" description="Smaller than average penis">small_penis</tag>
+    <tag display="Medium" description="Average-sized penis">medium_penis</tag>
+    <tag display="Large" description="Larger than average penis">large_penis</tag>
+    <tag display="Huge" paired="large_penis" description="Penis is extremely large">huge_penis</tag>
   </group>
   <group label="Penis Appearance" gender="male">
-    <tag display="Circumcised">circumcised</tag>
-    <tag display="Uncircumcised">uncircumcised</tag>
+    <tag display="Circumcised" description="Character has no foreskin">circumcised</tag>
+    <tag display="Uncircumcised" description="Character's foreskin is intact">uncircumcised</tag>
   </group>
   <group label="Non-Human Species" multiselect="true">
-    <tag display="Alien">alien</tag>
-    <tag display="Cat Person">catfolk</tag>
-    <tag display="Fairy" paired="spirit">fairy</tag>
-    <tag display="Monster">monster</tag>
-    <tag display="Undead" paired="monster">undead</tag>
-    <tag display="Non-Human">non-human</tag>
-    <tag display="Robot">robot</tag>
-    <tag display="Supernatural">supernatural</tag>
-    <tag display="Deity" paired="supernatural">deity</tag>
-    <tag display="Spirit" paired="supernatural">spirit</tag>
-    <tag display="Succubus">succubus</tag>
-	<tag display="Vampire">vampire</tag>
+    <tag display="Alien" description="Character is visibly extraterrestrial or freely mentions it">alien</tag>
+    <tag display="Cat Person" description="Character is a catgirl/catboy">catfolk</tag>
+    <tag display="Fairy" paired="spirit" description="Character is a fairy">fairy</tag>
+    <tag display="Monster" description="Character is of a classic monster species, i.e. vampires, succubi, lamias, harpies, etc.">monster</tag>
+    <tag display="Undead" paired="monster" description="Character was once living but died and is now animated by some other force">undead</tag>
+    <tag display="Non-Human" description="Character is visibly non-human">non-human</tag>
+    <tag display="Robot" description="Character is visibly a robot or a synthetic human">robot</tag>
+    <tag display="Supernatural" description="Character is of an otherworldly, paranormal, or religious origin, such as an angel, ghost, fairy, or genie">supernatural</tag>
+    <tag display="Deity" paired="supernatural" description="Character is a god or goddess of some sort">deity</tag>
+    <tag display="Spirit" paired="supernatural" description="Character is spectral or has a non-mortal essence">spirit</tag>
+    <tag display="Succubus" description="Character is a succubus or mentions being one">succubus</tag>
+    <tag display="Vampire" description="Character is a vampire">vampire</tag>
   </group>
   <group label="Features" multiselect="true">
-    <tag display="Animal Ears">animal_ears</tag>
-    <tag display="Tail">tail</tag>
-    <tag display="Winged">winged</tag>
-    <tag display="Horned">horned</tag>
-    <tag display="Pointy Ears">pointy_ears</tag>
+    <tag display="Animal Ears" description="Character has visible animal ears">animal_ears</tag>
+    <tag display="Tail" description="Character has a visible tail">tail</tag>
+    <tag display="Winged" description="Character has visible wings">winged</tag>
+    <tag display="Horned" description="Character has visible horns">horned</tag>
+    <tag display="Pointy Ears" description="Character has pointy ears">pointy_ears</tag>
   </group>
   <group label="Body Quirks" multiselect="true">
-    <tag display="Pierced Nipples">pierced_nipples</tag>
-    <tag display="Scarred">scarred</tag>
-    <tag display="Tattoo">tattoo</tag>
-    <tag display="Freckles">freckles</tag>
-	<tag display="Tanlines">tan_lines</tag>
+    <tag display="Pierced Nipples" description="Nipples are pierced">pierced_nipples</tag>
+    <tag display="Scarred" description="Visible scar(s)">scarred</tag>
+    <tag display="Tattoo" description="Visible tattoo(s)">tattoo</tag>
+    <tag display="Freckles" description="Visible freckles on either face or body">freckles</tag>
+    <tag display="Tanlines" description="Tan lines are visible">tan_lines</tag>
   </group>
   <group label="Weapon">
-    <tag display="Weapon">weapon</tag>
-    <tag display="Blade" paired="weapon">blade</tag>
-    <tag display="Gun" paired="weapon">gun</tag>
-    <tag display="Wand" paired="weapon">wand</tag>
+    <tag display="Weapon" description="Character carries a weapon of some sort">weapon</tag>
+    <tag display="Blade" paired="weapon" description="Character is carrying a sword, knife, or other bladed weapon">blade</tag>
+    <tag display="Gun" paired="weapon" description="Character is carrying a firearm">gun</tag>
+    <tag display="Wand" paired="weapon" description="Character carries a rod, staff, or other magical implement">wand</tag>
   </group>
   <group label="Clothing" multiselect="true">
-    <tag display="Armor">armor</tag>
-    <tag display="Bodysuit">bodysuit</tag>
-	<tag display="Cape">cape</tag>
-    <tag display="Choker">choker</tag>
-    <tag display="Formal Attire">formal_attire</tag>
-    <tag display="Glasses">glasses</tag>
-    <tag display="Hat">hat</tag>
-    <tag display="Leotard">leotard</tag>
-    <tag display="Masked">masked</tag>
-	<tag display="No Bra" gender="female">no_bra</tag>
-	<tag display="No Lower Underwear">no_underwear</tag>
-	<tag display="Pantyhose">pantyhose</tag>
-    <tag display="Skimpy Clothing">scantily-clad</tag>
-    <tag display="School Uniform">school_uniform</tag>
-	<tag display="Sunglasses">sunglasses</tag>
-	<tag display="Thigh-High Socks">thigh_highs</tag>
-    <tag display="Thong">thong</tag>
+    <tag display="Armor" description="Wearing some form of armor">armor</tag>
+    <tag display="Bodysuit" description="Wearing a full jumpsuit or bodysuit">bodysuit</tag>
+    <tag display="Cape" description="Wearing a cape">cape</tag>
+    <tag display="Choker" description="Wearing a choker">choker</tag>
+    <tag display="Formal Attire" description="Wearing eveningwear like a suit or fine dress. Not a school uniform.">formal_attire</tag>
+    <tag display="Glasses" description="Wearing glasses, but not sunglasses">glasses</tag>
+    <tag display="Hat" description="Wearing a hat">hat</tag>
+    <tag display="Leotard" description="Wearing a leotard">leotard</tag>
+    <tag display="Masked" description="Character's face is partially or completely covered by a mask">masked</tag>
+    <tag display="No Bra" gender="female" description="Character did not wear a bra to the game">no_bra</tag>
+    <tag display="No Lower Underwear" description="Character did not wear lower underwear (e.g. panties, boxers) to the game">no_underwear</tag>
+    <tag display="Pantyhose" description="Wearing pantyhose">pantyhose</tag>
+    <tag display="Skimpy Clothing" description="Character started the game with revealing or minimal clothing">scantily-clad</tag>
+    <tag display="School Uniform" description="Wearing a school uniform of some sort">school_uniform</tag>
+    <tag display="Sunglasses" description="Wearing sunglasses">sunglasses</tag>
+    <tag display="Thigh-High Socks" description="Wearing socks that reach the thigh">thigh_highs</tag>
+    <tag display="Thong" description="Wearing thong-style underwear">thong</tag>
   </group>
   <group label="Personality" multiselect="true">
-    <tag display="Aggressive">aggressive</tag>
-    <tag display="Cheerful">cheerful</tag>
-    <tag display="Confident">confident</tag>
-    <tag display="Ditzy">ditzy</tag>
-    <tag display="Exhibitionist">exhibitionist</tag>
-    <tag display="Fancy">fancy</tag>
-    <tag display="Fashionable">fashionable</tag>
-    <tag display="Gloomy">gloomy</tag>
-    <tag display="Indifferent">indifferent</tag>
-    <tag display="Innocent">innocent</tag>
-    <tag display="Insane">insane</tag>
-    <tag display="Kind">kind</tag>
-    <tag display="Lonely">lonely</tag>
-    <tag display="Mean">mean</tag>
-    <tag display="Moe/Adorable">moe</tag>
-    <tag display="Nerdy">nerdy</tag>
-    <tag display="Perverted">perverted</tag>
-    <tag display="Quiet">quiet</tag>
-    <tag display="Sarcastic">sarcastic</tag>
-    <tag display="Seductive">seductive</tag>
-    <tag display="Serious">serious</tag>
-    <tag display="Shut-In">shut_in</tag>
-    <tag display="Shy">shy</tag>
-    <tag display="Silent">silent</tag>
-    <tag display="Slutty">slutty</tag>
-    <tag display="Smart">smart</tag>
-    <tag display="Tomboy">tomboy</tag>
-    <tag display="Tsundere">tsundere</tag>
-    <tag display="Witty">witty</tag>
-    <tag display="Yandere">yandere</tag>
-    <tag display="Boob Envy" gender="female">boob_envy</tag>
-    <tag display="Penis Envy" gender="male">penis_envy</tag>
-    <tag display="Embarrassed Nude (F)" gender="female">enf</tag>
-    <tag display="Embarrassed Nude (M)" gender="male">enm</tag>
-    <tag display="Sexually Dominant">dominant</tag>
-    <tag display="Sexually Submissive">submissive</tag>
+    <tag display="Aggressive" description="Character is outright hostile and may regularly make threats of harm to the other players">aggressive</tag>
+    <tag display="Cheerful" description="Character is naturally happy and optimistic and just tries to have fun with the game">cheerful</tag>
+    <tag display="Confident" description="Character has little issue with their own nudity, may be proud of their body, and is unlikely to get embarrassed">confident</tag>
+    <tag display="Ditzy" description="Character is absent-minded, an airhead, or otherwise unaware of normal social behavior">ditzy</tag>
+    <tag display="Exhibitionist" description="Character actively wants to be seen naked and masturbating, and wants all eyes on them">exhibitionist</tag>
+    <tag display="Fancy" description="Character gives off an air of wealth and/or nobility">fancy</tag>
+    <tag display="Fashionable" description="Character is well-dress and always dresses for the occasion">fashionable</tag>
+    <tag display="Gloomy" description="Character is generally mournful and pessimistic">gloomy</tag>
+    <tag display="Indifferent" description="Character seems relatively neutral or uncaring about the game, or it might take a lot to get a reaction out of them">indifferent</tag>
+    <tag display="Innocent" description="Character is naive or unknowledgeable about sexual matters">innocent</tag>
+    <tag display="Insane" description="Character is clearly not of sound mind">insane</tag>
+    <tag display="Kind" description="Character is friendly and supportive to other characters">kind</tag>
+    <tag display="Lonely" description="Character has few, if any friends, but wants them. Maybe they're playing SPNatI to make some">lonely</tag>
+    <tag display="Mean" description="Character makes mean, rude, or harsh comments about other players">mean</tag>
+    <tag display="Moe/Adorable" description="Character has a conventially cute/adorable appearance and personality">moe</tag>
+    <tag display="Nerdy" description="Character has nerdy hobbies and/or interests">nerdy</tag>
+    <tag display="Perverted" description="Character has a fixation on sex, makes especially lewd comments, and is likely pretty kinky">perverted</tag>
+    <tag display="Quiet" description="Character doesn't talk much, and their sentences are usually brief">quiet</tag>
+    <tag display="Sarcastic" description="Character is sarcastic">sarcastic</tag>
+    <tag display="Seductive" description="Character has an alluring, sexy, femme-fatale or lady-killer manner, and will flirt with other characters">seductive</tag>
+    <tag display="Serious" description="Character has a no-nonsense personality and isn't one to make jokes">serious</tag>
+    <tag display="Shut-In" description="Character is a NEET or homebody and doesn't get out much">shut_in</tag>
+    <tag display="Shy" description="Character is shy in social situations and is likely reluctant to strip">shy</tag>
+    <tag display="Silent" description="Character does not speak at all">silent</tag>
+    <tag display="Slutty" description="Character is extremely sexually open, feels no shame, and will regularly make obscene advances/suggestions to other characters">slutty</tag>
+    <tag display="Smart" description="Character is highly knowledgeable or intelligent">smart</tag>
+    <tag display="Tomboy" description="Character is female but partakes in traditionally masculine pursuits and dresses as such.">tomboy</tag>
+    <tag display="Tsundere" description="Character starts out rude or hostile but opens up over the course of the game">tsundere</tag>
+    <tag display="Witty" description="Character is always ready to crack a joke">witty</tag>
+    <tag display="Yandere" description="Character is in murderously obsessive love with the player or another character">yandere</tag>
+    <tag display="Boob Envy" gender="female" description="Character is jealous of women with larger breasts than her and is insecure about her own chest">boob_envy</tag>
+    <tag display="Penis Envy" gender="male" description="Character is jealous of men with a larger penis than him and is insecure about his penis size">penis_envy</tag>
+    <tag display="Embarrassed Nude (F)" gender="female" description="Female that has a particularly adverse reaction to being naked">enf</tag>
+    <tag display="Embarrassed Nude (M)" gender="male" description="Male that has a particularly adverse reaction to being naked">enm</tag>
+    <tag display="Sexually Dominant" description="Character is sexually dominant">dominant</tag>
+    <tag display="Sexually Submissive" description="Character is sexually submissive">submissive</tag>
   </group>
   <group label="Sexual Orientation">
-    <tag display="Straight">straight</tag>
-    <tag display="Gay" gender="male">gay</tag>
-    <tag display="Lesbian" gender="female">lesbian</tag>
-    <tag display="Bisexual">bisexual</tag>
-    <tag display="Bi-curious">bi-curious</tag>
+    <tag display="Straight" description="Character is attracted to the opposite sex">straight</tag>
+    <tag display="Gay" gender="male" description="Male character is attracted to other males">gay</tag>
+    <tag display="Lesbian" gender="female" description="Female character is attracted to other females">lesbian</tag>
+    <tag display="Bisexual" description="Character is attracted to both sexes">bisexual</tag>
+    <tag display="Bi-curious" description="Character is predominantly attracted to the opposite sex, but is curious about the same sex, or is a straight character that opens up to same sex characters late in the game">bi-curious</tag>
   </group>
   <group label="Relationship">
-    <tag display="Single">single</tag>
-    <tag display="Taken/Open">taken</tag>
+    <tag display="Single" description="Not currently in a romantic/sexual relationship">single</tag>
+    <tag display="Taken/Open" description="Currently in a serious romantic/sexual relationship. However, it can still be an open relationship">taken</tag>
   </group>
   <group label="Nationality/Ethnicity" multiselect="true">
-    <tag display="African">african</tag>
-    <tag display="American">american</tag>
-    <tag display="Arabian">arabian</tag>
-    <tag display="Australian">australian</tag>
-    <tag display="Chinese">chinese</tag>
-    <tag display="English">english</tag>
-	<tag display="Finnish">finnish</tag>
-    <tag display="French">french</tag>
-	<tag display="German">german</tag>
-    <tag display="Hispanic">hispanic</tag>
-    <tag display="Italian">italian</tag>
-    <tag display="Japanese">japanese</tag>
-    <tag display="Korean">korean</tag>
-    <tag display="Scottish">scottish</tag>
-    <tag display="Swiss">swiss</tag>
-    <tag display="Mixed Heritage">mixed_heritage</tag>
+    <tag display="African" description="Of African origin or ethnicity">african</tag>
+    <tag display="American" description="From the United States">american</tag>
+    <tag display="Arabian" description="Of Middle Eastern origin or ethnicity">arabian</tag>
+    <tag display="Australian" description="Of Australian origin or ethnicity">australian</tag>
+    <tag display="Chinese" description="Of Chinese origin or ethnicity">chinese</tag>
+    <tag display="English" description="Of English origin or ethnicity">english</tag>
+    <tag display="Finnish" description="Of Finnish origin or ethnicity">finnish</tag>
+    <tag display="French" description="Of French origin or ethnicity">french</tag>
+    <tag display="German" description="Of German origin or ethnicity">german</tag>
+    <tag display="Hispanic" description="Of Spanish or Latin American origin or ethnicity">hispanic</tag>
+    <tag display="Italian" description="Of Italian origin or ethnicity">italian</tag>
+    <tag display="Japanese" description="Of Japanese origin or ethnicity">japanese</tag>
+    <tag display="Korean" description="Of South Korean origin or ethnicity">korean</tag>
+    <tag display="Mexican" description="Of Mexican origin or ethnicity">mexican</tag>
+    <tag display="Scottish" description="Of Scottish origin or ethnicity">scottish</tag>
+    <tag display="Swiss" description="Of Swiss origin or ethnicity">swiss</tag>
+    <tag display="Mixed Heritage" description="Of mixed ethnicity or nationality">mixed_heritage</tag>
   </group>
   <group label="Universe/Organization" multiselect="true">
-    <tag display="Mystery Inc.">mystery_inc</tag>
-    <tag display="Phantom Thieves">phantom_thieves</tag>
-    <tag display="UA Academy">ua_academy</tag>
-    <tag display="Overwatch">overwatch</tag>
-	<tag display="Team CFVY">cfvy</tag>
-	<tag display="Team JNPR">jnpr</tag>
-    <tag display="Team RWBY">rwby</tag>
-    <tag display="Teen Titans">teen_titans</tag>
-    <tag display="Avengers">avengers</tag>
-	<tag display="X-Men">x-men</tag>
-    <tag display="Danganronpa 1">dr1</tag>
-    <tag display="Danganronpa 2">dr2</tag>
-    <tag display="Danganronpa V3">drv3</tag>
-	<tag display="Honnouji Academy">honnouji_academy</tag>
-	<tag display="Starfleet">starfleet</tag>
-    <tag display="Kanto (Pokémon)">kanto</tag>
-    <tag display="Johto (Pokémon)">johto</tag>
-    <tag display="Hoenn (Pokémon)">hoenn</tag>
-    <tag display="Sinnoh (Pokémon)">sinnoh</tag>
-    <tag display="Unova (Pokémon)">unova</tag>
-    <tag display="Kalos (Pokémon)">kalos</tag>
-    <tag display="Alola (Pokémon)">alola</tag>
+    <tag display="Mystery Inc." description="A member of the Scooby Doo gang">mystery_inc</tag>
+    <tag display="Phantom Thieves" description="A member of Persona 5's Phantom Thieves">phantom_thieves</tag>
+    <tag display="UA Academy" description="Student at UA High in My Hero Academia">ua_academy</tag>
+    <tag display="Overwatch" description="Member of Overwatch in the game of the same name">overwatch</tag>
+    <tag display="Team CFVY" description="Member of Team CFVY in RWBY">cfvy</tag>
+    <tag display="Team JNPR" description="Member of team JNPR in RWBY">jnpr</tag>
+    <tag display="Team RWBY" description="Member of team RWBY in RWBY">rwby</tag>
+    <tag display="Teen Titans" description="Member of the Team Titans">teen_titans</tag>
+    <tag display="Avengers" description="Member of the Avengers">avengers</tag>
+    <tag display="X-Men" description="Member of the X-Men">x-men</tag>
+    <tag display="Danganronpa 1" description="Class member from the first Danganronpa game">dr1</tag>
+    <tag display="Danganronpa 2" description="Class member from Danganronpa 2">dr2</tag>
+    <tag display="Danganronpa V3" description="Class member from Danganronpa V3">drv3</tag>
+    <tag display="Honnouji Academy" description="Student at Honnouji Academy in Kill la Kill">honnouji_academy</tag>
+    <tag display="Starfleet" description="Belongs to the Starfleet organization in Star Trek">starfleet</tag>
+    <tag display="Kanto (Pokémon)" description="From Pokémon's Kanto region">kanto</tag>
+    <tag display="Johto (Pokémon)" description="From Pokémon's Johto region">johto</tag>
+    <tag display="Hoenn (Pokémon)" description="From Pokémon's Hoenn region">hoenn</tag>
+    <tag display="Sinnoh (Pokémon)" description="From Pokémon's Sinnoh region">sinnoh</tag>
+    <tag display="Unova (Pokémon)" description="From Pokémon's Unova region">unova</tag>
+    <tag display="Kalos (Pokémon)" description="From Pokémon's Kalos region">kalos</tag>
+    <tag display="Alola (Pokémon)" description="From Pokémon's Alola region">alola</tag>
   </group>
   <group label="Genre" multiselect="true">
-    <tag display="Fantasy">fantasy</tag>
-    <tag display="Future">future</tag>
-    <tag display="Post-Apocalyptic">post-apocalyptic</tag>
-    <tag display="Science Fiction">sci-fi</tag>
-    <tag display="Space">space</tag>
+    <tag display="Fantasy" description="From a fantasy setting">fantasy</tag>
+    <tag display="Future" description="From a futuristic setting">future</tag>
+    <tag display="Post-Apocalyptic" description="From a post-apocalyptic setting">post-apocalyptic</tag>
+    <tag display="Horror"  description="From a horror genre">horror</tag>
+    <tag display="Science Fiction" description="From a science fiction setting">sci-fi</tag>
+    <tag display="Space" description="Character lives in space">space</tag>
   </group>
   <group label="Source Medium" multiselect="true">
-    <tag display="Anime">anime</tag>
-    <tag display="Book">book</tag>
-    <tag display="Cartoon">cartoon</tag>
-    <tag display="Comic">comic</tag>
-    <tag display="Hentai">hentai</tag>
-    <tag display="Internet Meme">internet_meme</tag>
-    <tag display="Manga">manga</tag>
-    <tag display="Movie">movie</tag>
-    <tag display="TV">tv_show</tag>
-    <tag display="Video Game">video_game</tag>
-    <tag display="Visual Novel">visual_novel</tag>
-    <tag display="Website">website</tag>
+    <tag display="Anime" description="Originates from Japanese animation">anime</tag>
+    <tag display="Book" description="Originates from literature not including comics, manga, or graphic novels">book</tag>
+    <tag display="Cartoon" description="Originates from Western animation">cartoon</tag>
+    <tag display="Comic" description="Originates from Western comic books or graphic novels">comic</tag>
+    <tag display="Hentai" description="Originates from a hentai game, anime or manga">hentai</tag>
+    <tag display="Internet Meme" description="Originates from an Internet meme">internet_meme</tag>
+    <tag display="Manga" description="Originates from a Japanese comic or graphic novel">manga</tag>
+    <tag display="Movie" description="Originates from a film or movie">movie</tag>
+    <tag display="TV" description="Originates from a live-action TV show">tv_show</tag>
+    <tag display="Video Game" description="Originates from a video game">video_game</tag>
+    <tag display="Visual Novel" description="Originates from a visual novel">visual_novel</tag>
+    <tag display="Website" description="Originates from a website (ex. a site mascot)">website</tag>
   </group>
   <group label="Source Material" multiselect="true">
-    <tag display=".hack" paired="video_game">dot_hack</tag>
-    <tag display="Ace Attorney" paired="capcom,video_game,visual_novel">ace_attorney</tag>
-    <tag display="Adventure Time" paired="cartoon">adventure_time</tag>
-    <tag display="Alice: Madness Returns" paired="video_game">alice_madness_returns</tag>
-	<tag display="ARMS" paired="nintendo,video_game">arms_franchise</tag>
-	<tag display="Attack on Titan" paired="anime,manga,fantasy">attack_on_titan</tag>
-    <tag display="Batman" paired="dc_comics,comic">batman</tag>
-	<tag display="Battleborn" paired="video_game">battleborn</tag>
-    <tag display="Big Hero 6" paired="cartoon,disney,movie,marvel">big_hero_6</tag>
-    <tag display="Bioshock" paired="video_game">bioshock</tag>
-    <tag display="Black Lagoon" paired="anime,manga">black_lagoon</tag>
-	<tag display="Borderlands" paired="video_game,future,sci-fi">borderlands</tag>
-    <tag display="Bravely Default" paired="video_game">bravely_default</tag>
-    <tag display="Bravely Second" paired="bravely_default,video_game">bravely_second</tag>
-    <tag display="Buffy the Vampire Slayer" paired="tv_show">buffy_the_vampire_slayer</tag>
-    <tag display="Capcom" paired="video_game">capcom</tag>
-    <tag display="Clannad" paired="visual_novel,anime">clannad</tag>
-    <tag display="Community" paired="tv_show">community</tag>
-	<tag display="Cowboy Bebop" paired="anime,future,sci-fi">cowboy_bebop"</tag>
-    <tag display="Crush Crush" paired="video_game">crush_crush</tag>
-	<tag display="Danganronpa" paired="video_game,visual_novel">danganronpa</tag>
-    <tag display="Daria" paired="cartoon">daria_franchise</tag>
-    <tag display="Darkstalkers" paired="video_game">darkstalkers</tag>
-    <tag display="DC Comics" paired="comic">dc_comics</tag>
-    <tag display="Dead by Daylight" paired="video_game,horror">dead_by_daylight</tag>
-	<tag display="Dead or Alive" paired="video_game">dead_or_alive</tag>
-    <tag display="Disney">disney</tag>
-	<tag display="Doki Doki Literature Club" paired="visual_novel">ddlc</tag>
-    <tag display="Dragon Ball" paired="anime,manga">dragon_ball</tag>
-    <tag display="Elena of Avalor" paired="cartoon,disney">elena_of_avalor</tag>
-    <tag display="Embla Academy" paired="visual_novel">embla_academy</tag>
-    <tag display="Emergence" paired="hentai,manga">emergence</tag>
-	<tag display="Etrian Odyssey" paired="video_game">etrian_odyssey</tag>
-    <tag display="Evangelion" paired="anime,future,sci-fi">evangelion</tag>
-    <tag display="F-Zero" paired="nintendo,video_game,future,sci-fi">f_zero</tag>
-	<tag display="Fallout" paired="video_game,future,post-apocalyptic,sci-fi">fallout</tag>
-    <tag display="Final Fantasy" paired="video_game">final_fantasy</tag>
-    <tag display="Final Fantasy VII" paired="video_game">final_fantasy_7</tag>
-    <tag display="Final Fantasy VIII" paired="video_game">final_fantasy_8</tag>
-    <tag display="Fire Emblem" paired="nintendo,video_game,fantasy">fire_emblem</tag>
-	<tag display="Fire Emblem: Awakening" paired="nintendo,fire_emblem,video_game,fantasy">fe_awakening</tag>
-	<tag display="Fire Emblem: Blazing Blade" paired="nintendo,fire_emblem,video_game,fantasy">fe_blazing_blade</tag>
-	<tag display="Fire Emblem: Fates" paired="nintendo,fire_emblem,video_game,fantasy">fe_fates</tag>
-	<tag display="Fire Emblem: Shadow Dragon" paired="nintendo,fire_emblem,video_game,fantasy">fe_shadow_dragon</tag>
-	<tag display="Five Nights at Freddy's" paired="video_game,horror">fnaf</tag>
-	<tag display="Futurama" paired="cartoon,sci-fi,future">futurama</tag>
-	<tag display="Girls' Frontline" paired="video_game,sci-fi,post-apocalyptic">girls_frontline</tag>
-    <tag display="Gundam" paired="anime,sci-fi,future">gundam</tag>
-    <tag display="Harry Potter" paired="book,fantasy">harry_potter_franchise</tag>
-    <tag display="Homestuck" paired="comic">homestuck</tag>
-	<tag display="Hotel Transylvania" paired="cartoon,movie">hotel_transylvania</tag>
-    <tag display="HuniePop" paired="video_game">huniepop</tag>
-	<tag display="Jojo's Bizarre Adventure" paired="anime,manga">jjba</tag>
-	<tag display="Kantai Collection" paired="video_game,anime">kantai_collection</tag>
-	<tag display="Katawa Shoujo" paired="visual_novel">katawa_shoujo</tag>
-	<tag display="Khonjin house" paired="cartoon">khonjin_house</tag>
-    <tag display="Kid Icarus" paired="nintendo,video_game">kid_icarus</tag>
-	<tag display="Kill la Kill" paired="anime,sci-fi,studio_trigger">kill_la_kill</tag>
-    <tag display="Kim Possible" paired="cartoon">kim_possible_franchise</tag>
-	<tag display="League of Legends" paired="video_game">league_of_legends</tag>
-    <tag display="Left 4 Dead" paired="valve,video_game,horror,post-apocalyptic">left_4_dead</tag>
-    <tag display="Legend of Dark Witch" paired="video_game">legend_of_dark_witch</tag>
-	<tag display="Legend of Korra" paired="cartoon">legend_of_korra</tag>
-    <tag display="Legend of Zelda" paired="nintendo,video_game,fantasy">legend_of_zelda</tag>
-	<tag display="Little Witch Academia" paired="anime,fantasy,studio_trigger">little_witch_academia</tag>
-    <tag display="Lord of the Rings" paired="book,movie,fantasy">lotr</tag>
-	<tag display="Mahou Shoujo Madoka Magica" paired="anime">madoka_magica"</tag>
-    <tag display="Marvel Comics" paired="comic">marvel</tag>
-    <tag display="Marvel Cinematic Universe" paired="marvel">mcu</tag>
-    <tag display="Metroid" paired="nintendo,video_game,space,sci-fi,future">metroid</tag>
-    <tag display="Miraculous" paired="cartoon">miraculous</tag>
-    <tag display="Monkey Island" paired="video_game">monkey_island</tag>
-	<tag display="Monster Girl Quest" paired="video_game,hentai">monster_girl_quest</tag>
-    <tag display="Mortal Kombat" paired="video_game">mortal_kombat</tag>
-    <tag display="My Hero Academia" paired="anime,manga,future,sci-fi">my_hero_academia</tag>
-    <tag display="My Little Pony" paired="cartoon">my_little_pony</tag>
-	<tag display="My Little Pony: Equestria Girls" paired="cartoon">equestria_girls</tag>
-    <tag display="Naruto" paired="anime,manga">naruto_franchise</tag>
-    <tag display="NieR: Automata" paired="video_game,future,sci-fi,post-apocalyptic">nier_automata</tag>
-    <tag display="Nintendo" paired="video_game">nintendo</tag>
-	<tag display="Nonary Games" paired="video_game">nonary_games</tag>
-	<tag display="Original Character">original_character</tag>
-    <tag display="Overwatch" paired="video_game,future,sci-fi">overwatch_franchise</tag>
-	<tag display="Paladins" paired="video_game">paladins</tag>
-	<tag display="Persona" paired="video_game">persona</tag>
-    <tag display="Persona 5" paired="persona,video_game,anime,sci-fi">persona_5</tag>
-    <tag display="Pokemon" paired="nintendo">pokemon</tag>
-    <tag display="Portal" paired="valve,video_game">portal</tag>
-	<tag display="Power Rangers" paired="tv_show">power_rangers</tag>
-	<tag display="Resident Evil" paired="video_game,horror">resident_evil</tag>
-    <tag display="Rosario to Vampire" paired="anime,manga">rosario_to_vampire</tag>
-    <tag display="RWBY Franchise" paired="cartoon,fantasy">rwby_franchise</tag>
-	<tag display="Scooby Doo" paired="cartoon">scooby_doo</tag>
-    <tag display="Shantae" paired="video_game">shantae_franchise</tag>
-    <tag display="SNK" paired="video_game">snk</tag>
-    <tag display="Smash Bros." paired="video_game">smash_bros</tag>
-	<tag display="Sonic the Hedgehog" paired="video_game">sonic_franchise</tag>
-	<tag display="Spooky's Jump Scare Mansion" paired="video_game,horror">spookys_mansion</tag>
-    <tag display="Star Trek" paired="tv_show,sci-fi,future,space">star_trek</tag>
-	<tag display="Star Wars" paired="movie,sci-fi,space">star_wars</tag>
-    <tag display="Stardew Valley" paired="video_game">stardew_valley</tag>
-	<tag display="StarForce-One" paired="book">starforce-one</tag>
-	<tag display="Steven Universe" paired="cartoon">steven_universe</tag>
-    <tag display="Street Fighter" paired="video_game">street_fighter</tag>
-	<tag display="Studio Trigger" paired="anime">studio_trigger</tag>
-	<tag display="Super Mario" paired="nintendo,video_game">super_mario</tag>
-    <tag display="Sword Art Online" paired="anime">sword_art_online</tag>
-	<tag display="Tales of Series" paired="video_game">tales_of</tag>
-	<tag display="Tales of Graces" paired="tales_of,video_game">tales_of_graces</tag>
-	<tag display="Tales of Symphonia" paired="tales_of,video_game">tales_of_symphonia</tag>
-    <tag display="Team Fortress 2" paired="valve,video_game">team_fortress_2</tag>
-    <tag display="Teen Titans" paired="cartoon,dc_comics,comic">teen_titans_franchise</tag>
-    <tag display="Teenage Mutant Ninja Turtles" paired="cartoon">tmnt</tag>
-	<tag display="Tengen Toppa Gurren Lagann" paired="anime,sci-fi,post-apocalyptic,studio_trigger">ttgl</tag>
-	<tag display="The Witcher" paired="video_game,fantasy">the_witcher</tag>
-	<tag display="Tomb Raider" paired="video_game">tomb_raider</tag>
-    <tag display="Toradora" paired="anime">toradora</tag>
-	<tag display="Touhou Project" paired="video_game,fantasy">touhou_project</tag>
-	<tag display="Ty the Tasmanian Tiger" paired="video_game">ty_franchise</tag>
-    <tag display="Undertale" paired="video_game">undertale</tag>
-	<tag display="VA-11 HALL-A" paired="video_game,visual_novel,sci-fi,future">va-11_hall-a</tag>
-    <tag display="Vandread" paired="anime,space,sci-fi,future">vandread</tag>
-    <tag display="Valve" paired="video_game">valve</tag>
-	<tag display="Wakfu" paired="cartoon,fantasy">wakfu</tag>
-    <tag display="Wii Fit" paired="nintendo,video_game">wii_fit</tag>
-	<tag display="World Trigger" paired="anime,manga,sci-fi">world_trigger</tag>
-	<tag display="Xenoblade Chronicles" paired="video_game">xenoblade_chronicles</tag>
-	<tag display="Yandere Simulator" paired="video_game">yandere_simulator</tag>
-    <tag display="Yu-Gi-Oh!" paired="anime">yugioh</tag>
+    <tag display=".hack" paired="video_game" description="From the .hack game series">dot_hack</tag>
+    <tag display="Ace Attorney" paired="capcom,video_game,visual_novel" description="From the Ace Attorney game series">ace_attorney</tag>
+    <tag display="Adventure Time" paired="cartoon" description="From the Adventure Time franchise">adventure_time</tag>
+    <tag display="Alice: Madness Returns" paired="video_game" description="From American McGee's Alice game">alice_madness_returns</tag>
+    <tag display="ARMS" paired="nintendo,video_game" description="From the video game ARMS">arms_franchise</tag>
+    <tag display="Attack on Titan" paired="anime,manga,fantasy" description="From the Attack on Titan series">attack_on_titan</tag>
+    <tag display="Batman" paired="dc_comics,comic" description="From DC's Batman franchise">batman</tag>
+    <tag display="Battleborn" paired="video_game" description="From the video game Batteborn">battleborn</tag>
+    <tag display="Big Hero 6" paired="cartoon,disney,movie,marvel" description="From Disney's Big Hero 6">big_hero_6</tag>
+    <tag display="Bioshock" paired="video_game" description="From the Bioshock game series">bioshock</tag>
+    <tag display="Black Lagoon" paired="anime,manga" description="From the manga/anime Black Lagoon">black_lagoon</tag>
+    <tag display="Borderlands" paired="video_game,future,sci-fi" description="From the Borderlands game series">borderlands</tag>
+    <tag display="Bravely Default" paired="video_game" description="From the Bravely game series">bravely_default</tag>
+    <tag display="Bravely Second" paired="bravely_default,video_game" description="From the game Bravely Second">bravely_second</tag>
+    <tag display="Buffy the Vampire Slayer" paired="tv_show" description="From the TV show Buffy the Vampire Slayer">buffy_the_vampire_slayer</tag>
+    <tag display="Capcom" paired="video_game" description="From a game owned by Capcom">capcom</tag>
+    <tag display="Clannad" paired="visual_novel,anime" description="From the visual novel/anime Clannad">clannad</tag>
+    <tag display="Community" paired="tv_show" description="From the TV series Community">community</tag>
+    <tag display="Cowboy Bebop" paired="anime,future,sci-fi" description="From the anime Cowboy Bebop">cowboy_bebop"</tag>
+    <tag display="Crush Crush" paired="video_game" description="From the game Crush Crush">crush_crush</tag>
+    <tag display="Danganronpa" paired="video_game,visual_novel" description="From the Danganronpa game series">danganronpa</tag>
+    <tag display="Daria" paired="cartoon" description="From the animated series Daria">daria_franchise</tag>
+    <tag display="Darkstalkers" paired="video_game" description="From the Darkstalkers game series">darkstalkers</tag>
+    <tag display="DC Comics" paired="comic" description="Part of the DC Comics franchise">dc_comics</tag>
+    <tag display="Dead by Daylight" paired="video_game,horror" description="From the Dead by Daylight video game">dead_by_daylight</tag>
+    <tag display="Dead or Alive" paired="video_game" description="From the Dead or Alive game franchise">dead_or_alive</tag>
+    <tag display="Disney" description="From a Disney property, but not Marvel or Star Wars">disney</tag>
+    <tag display="Doki Doki Literature Club" paired="visual_novel" description="From the VN Doki Doki Literature Club">ddlc</tag>
+    <tag display="Dragon Ball" paired="anime,manga" description="From the Dragon Ball franchise, including DBZ, Super, GT, etc.">dragon_ball</tag>
+    <tag display="Elena of Avalor" paired="cartoon,disney" description="From the animated series Elena of Avalor">elena_of_avalor</tag>
+    <tag display="Embla Academy" paired="visual_novel" description="From Embla Academy, based on Fire Emblem Fates">embla_academy</tag>
+    <tag display="Emergence" paired="hentai,manga" description="From the hentai manga Emergence">emergence</tag>
+    <tag display="Etrian Odyssey" paired="video_game" description="From the Etrian Odyssey games">etrian_odyssey</tag>
+    <tag display="Evangelion" paired="anime,future,sci-fi" description="From the anime franchise Evangelion">evangelion</tag>
+    <tag display="F-Zero" paired="nintendo,video_game,future,sci-fi" description="From the F-Zero game series">f_zero</tag>
+    <tag display="Fallout" paired="video_game,future,post-apocalyptic,sci-fi" description="From the Fallout game franchise">fallout</tag>
+    <tag display="Final Fantasy" paired="video_game" description="From the Final Fantasy game franchise">final_fantasy</tag>
+    <tag display="Final Fantasy VII" paired="video_game" description="From Final Fantasy VII">final_fantasy_7</tag>
+    <tag display="Final Fantasy VIII" paired="video_game" description="From Final Fantasy VIII">final_fantasy_8</tag>
+    <tag display="Fire Emblem" paired="nintendo,video_game,fantasy" description="From the Fire Emblem series">fire_emblem</tag>
+    <tag display="Fire Emblem: Awakening" paired="nintendo,fire_emblem,video_game,fantasy" description="From Fire Emblem: Awakening">fe_awakening</tag>
+    <tag display="Fire Emblem: Blazing Blade" paired="nintendo,fire_emblem,video_game,fantasy" description="From Fire Emblem: Blazing Blade">fe_blazing_blade</tag>
+    <tag display="Fire Emblem: Fates" paired="nintendo,fire_emblem,video_game,fantasy" description="From Fire Emblem: Fates">fe_fates</tag>
+    <tag display="Fire Emblem: Shadow Dragon" paired="nintendo,fire_emblem,video_game,fantasy" description="From Fire Emblem: Shadow Dragon">fe_shadow_dragon</tag>
+    <tag display="Five Nights at Freddy's" paired="video_game,horror" description="From the Five Nights at Freddy's game series">fnaf</tag>
+    <tag display="Futurama" paired="cartoon,sci-fi,future" description="From the animated series Futurama">futurama</tag>
+    <tag display="Girls' Frontline" paired="video_game,sci-fi,post-apocalyptic" description="From the Girls' Frontline game series">girls_frontline</tag>
+    <tag display="Gundam" paired="anime,sci-fi,future" description="From the Gundam anime franchise">gundam</tag>
+    <tag display="Harry Potter" paired="book,fantasy" description="From the Harry Potter universe">harry_potter_franchise</tag>
+    <tag display="Homestuck" paired="comic" description="From the webcomic Homestuck">homestuck</tag>
+    <tag display="Hotel Transylvania" paired="cartoon,movie" description="From the movie series Hotel Transylvania">hotel_transylvania</tag>
+    <tag display="HuniePop" paired="video_game" description="From the HuniePop game series">huniepop</tag>
+    <tag display="Jojo's Bizarre Adventure" paired="anime,manga" description="From the anime/manga Jojo's Bizarre Adventure">jjba</tag>
+    <tag display="Kantai Collection" paired="video_game,anime" description="From the Kantai Collection franchise">kantai_collection</tag>
+    <tag display="Katawa Shoujo" paired="visual_novel" description="From VN Katawa Shoujo">katawa_shoujo</tag>
+    <tag display="Khonjin house" paired="cartoon" description="From the animated series Khonjin House">khonjin_house</tag>
+    <tag display="Kid Icarus" paired="nintendo,video_game" description="From the Kid Icarus game series">kid_icarus</tag>
+    <tag display="Kill la Kill" paired="anime,sci-fi,studio_trigger" description="From the anime Kill la Kill">kill_la_kill</tag>
+    <tag display="Kim Possible" paired="cartoon" description="From the animated series Kim Possible">kim_possible_franchise</tag>
+    <tag display="League of Legends" paired="video_game" description="From the game League of Legends">league_of_legends</tag>
+    <tag display="Left 4 Dead" paired="valve,video_game,horror,post-apocalyptic" description="From the Left 4 Dead game series">left_4_dead</tag>
+    <tag display="Legend of Dark Witch" paired="video_game" description="From the Legend of Dark Witch game franchise">legend_of_dark_witch</tag>
+    <tag display="Legend of Korra" paired="cartoon" description="From the animated series Legend of Korra">legend_of_korra</tag>
+    <tag display="Legend of Zelda" paired="nintendo,video_game,fantasy" description="From the Legend of Zelda game franchise">legend_of_zelda</tag>
+    <tag display="Little Witch Academia" paired="anime,fantasy,studio_trigger" description="From the anime Little Witch Academia">little_witch_academia</tag>
+    <tag display="Lord of the Rings" paired="book,movie,fantasy" description="From the Lord of the Rings franchise">lotr</tag>
+    <tag display="Mahou Shoujo Madoka Magica" paired="anime" description="From the anime Puella Magi Madoka Magica">madoka_magica"</tag>
+    <tag display="Marvel Comics" paired="comic" description="From a comic book series owned by Marvel">marvel</tag>
+    <tag display="Marvel Cinematic Universe" paired="marvel" description="From the Marvel Cinematic Universe">mcu</tag>
+    <tag display="Metroid" paired="nintendo,video_game,space,sci-fi,future" description="From the Metroid games">metroid</tag>
+    <tag display="Miraculous" paired="cartoon" description="From the animated series Miraculous">miraculous</tag>
+    <tag display="Monkey Island" paired="video_game" description="From the Monkey Island series of video games">monkey_island</tag>
+    <tag display="Monster Girl Quest" paired="video_game,hentai" description="From the H-game series Monster Girl Quest">monster_girl_quest</tag>
+    <tag display="Mortal Kombat" paired="video_game" description="From the Mortal Kombat series of video games">mortal_kombat</tag>
+    <tag display="My Hero Academia" paired="anime,manga,future,sci-fi" description="From the anime/manga series My Hero Academia">my_hero_academia</tag>
+    <tag display="My Little Pony" paired="cartoon" description="From the animated series My Little Pony">my_little_pony</tag>
+    <tag display="My Little Pony: Equestria Girls" paired="cartoon" description="From the animated series My Little Pony: Equestria Girls">equestria_girls</tag>
+    <tag display="Naruto" paired="anime,manga" description="From the anime/manga series Naruto">naruto_franchise</tag>
+    <tag display="NieR: Automata" paired="video_game,future,sci-fi,post-apocalyptic" description="From the video game NieR: Automata">nier_automata</tag>
+    <tag display="Nintendo" paired="video_game" description="From a video game franchise owned by Nintendo">nintendo</tag>
+    <tag display="Nonary Games" paired="video_game" description="From the Nonary series of video games, also known as Zero Escape">nonary_games</tag>
+    <tag display="Original Character" description="An original creation not from an established work">original_character</tag>
+    <tag display="Overwatch" paired="video_game,future,sci-fi" description="From the Overwatch video game franchise">overwatch_franchise</tag>
+    <tag display="Paladins" paired="video_game" description="From the Paladins video game series">paladins</tag>
+    <tag display="Persona" paired="video_game" description="From the Persona series of video games">persona</tag>
+    <tag display="Persona 4" paired="persona,video_game,anime,sci-fi" description="From the video game Persona 4">persona_4</tag>
+    <tag display="Persona 5" paired="persona,video_game,anime,sci-fi" description="From the video game Persona 5">persona_5</tag>
+    <tag display="Pokemon" paired="nintendo" description="From the Pokemon franchise">pokemon</tag>
+    <tag display="Portal" paired="valve,video_game" description="From the Portal game series">portal</tag>
+    <tag display="Power Rangers" paired="tv_show" description="From the Power Rangers franchise">power_rangers</tag>
+    <tag display="Resident Evil" paired="video_game,horror" description="From the Resident Evil series">resident_evil</tag>
+    <tag display="Rosario to Vampire" paired="anime,manga" description="From the anime/manga Rosario to Vampire">rosario_to_vampire</tag>
+    <tag display="RWBY Franchise" paired="cartoon,fantasy" description="From the animated series RWBY">rwby_franchise</tag>
+    <tag display="Scooby Doo" paired="cartoon" description="From the Scooby Doo cartoons">scooby_doo</tag>
+    <tag display="Shantae" paired="video_game" description="From the Shantae video games">shantae_franchise</tag>
+    <tag display="SNK" paired="video_game" description="From the SNK series of video games">snk</tag>
+    <tag display="Smash Bros." paired="video_game" description="From Nintendo's Smash Bros. series">smash_bros</tag>
+    <tag display="Sonic the Hedgehog" paired="video_game" description="From the Sonic the Hedgehog franchise">sonic_franchise</tag>
+    <tag display="Spooky's Jump Scare Mansion" paired="video_game,horror" description="From the game Spooky's House of Jumpscares">spookys_mansion</tag>
+    <tag display="Star Trek" paired="tv_show,sci-fi,future,space" description="From the Star Trek franchise">star_trek</tag>
+    <tag display="Star Wars" paired="movie,sci-fi,space" description="From the Star Wars franchise">star_wars</tag>
+    <tag display="Stardew Valley" paired="video_game" description="From the game Stardew Valley">stardew_valley</tag>
+    <tag display="StarForce-One" paired="book" description="From the novel Starforce-one">starforce-one</tag>
+    <tag display="Steven Universe" paired="cartoon" description="From the animated series Steven Universe">steven_universe</tag>
+    <tag display="Street Fighter" paired="video_game" description="From the Street Fighter series of video games">street_fighter</tag>
+    <tag display="Studio Trigger" paired="anime" description="From a series by Studio Trigger">studio_trigger</tag>
+    <tag display="Super Mario" paired="nintendo,video_game" description="From the Mario video game franchise">super_mario</tag>
+    <tag display="Sword Art Online" paired="anime" description="From the anime Sword Art Online">sword_art_online</tag>
+    <tag display="Tales of Series" paired="video_game" description="From a game in the Tales series">tales_of</tag>
+    <tag display="Tales of Graces" paired="tales_of,video_game" description="From the game Tales of Graces">tales_of_graces</tag>
+    <tag display="Tales of Symphonia" paired="tales_of,video_game" description="From the game Tales of Symphonia">tales_of_symphonia</tag>
+    <tag display="Team Fortress 2" paired="valve,video_game" description="From the game Team Fortress 2">team_fortress_2</tag>
+    <tag display="Teen Titans" paired="cartoon,dc_comics,comic" description="From the Teen Titans franchise">teen_titans_franchise</tag>
+    <tag display="Teenage Mutant Ninja Turtles" paired="cartoon" description="From the Teenage Mutant Ninja Turtles franchise">tmnt</tag>
+    <tag display="Tengen Toppa Gurren Lagann" paired="anime,sci-fi,post-apocalyptic,studio_trigger" description="From the anime Tengen Toppa Gurren Lagann">ttgl</tag>
+    <tag display="The Witcher" paired="video_game,fantasy" description="From The Witcher series of video games">the_witcher</tag>
+    <tag display="Tomb Raider" paired="video_game" description="From the Tomb Raider franchise">tomb_raider</tag>
+    <tag display="Toradora" paired="anime" description="From the anime Toradora">toradora</tag>
+    <tag display="Touhou Project" paired="video_game,fantasy" description="From the Touhou Project series of games">touhou_project</tag>
+    <tag display="Ty the Tasmanian Tiger" paired="video_game" description="From the TY the Tasmanian Tiger games">ty_franchise</tag>
+    <tag display="Undertale" paired="video_game" description="From the video game Undertale">undertale</tag>
+    <tag display="VA-11 HALL-A" paired="video_game,visual_novel,sci-fi,future" description="From the visual novel/game VA-11 HALL-A">va-11_hall-a</tag>
+    <tag display="Vandread" paired="anime,space,sci-fi,future" description="From the anime Vandread">vandread</tag>
+    <tag display="Valve" paired="video_game" description="From a game series owned by Valve">valve</tag>
+    <tag display="Wakfu" paired="cartoon,fantasy" description="From the Wakfu series">wakfu</tag>
+    <tag display="Wii Fit" paired="nintendo,video_game" description="From the video game Wii Fit">wii_fit</tag>
+    <tag display="World Trigger" paired="anime,manga,sci-fi" description="From the anime/manga World Trigger">world_trigger</tag>
+    <tag display="Xenoblade Chronicles" paired="video_game" description="From the Xenoblade Chronicles series">xenoblade_chronicles</tag>
+    <tag display="Yandere Simulator" paired="video_game" description="From the video game Yandere Simulator">yandere_simulator</tag>
+    <tag display="Yu-Gi-Oh!" paired="anime" description="From the anime Yi-Gi-Oh!">yugioh</tag>
   </group>
   <group label="Occupation" multiselect="true">
-    <tag display="Adventurer">adventurer</tag>
-    <tag display="Bounty Hunter">bounty_hunter</tag>
-    <tag display="Celebrity">celebrity</tag>
-    <tag display="Clown">clown</tag>
-    <tag display="Criminal">criminal</tag>
-    <tag display="Detective">detective</tag>
-    <tag display="Gamer">gamer</tag>
-    <tag display="Lawyer">lawyer</tag>
-    <tag display="Fighter">fighter</tag>
-    <tag display="Journalist">journalist</tag>
-    <tag display="Magical Girl">magical_girl</tag>
-    <tag display="Mech Pilot">mech_pilot</tag>
-    <tag display="Medic">medic</tag>
-    <tag display="Military">military</tag>
-    <tag display="Model">model</tag>
-    <tag display="Musician">musician</tag>
-    <tag display="Pirate">pirate</tag>
-    <tag display="Police">police</tag>
-    <tag display="Prince">prince</tag>
-    <tag display="Princess">princess</tag>
-    <tag display="Scientist">scientist</tag>
-    <tag display="Superhero">superhero</tag>
-    <tag display="Spy">spy</tag>
-    <tag display="Student">student</tag>
-    <tag display="Witch">witch</tag>
+    <tag display="Adventurer" description="Goes on adventures around their world, likely in a traditional RPG/Fantasy sense">adventurer</tag>
+    <tag display="Bounty Hunter" description="Works as a bounty hunter">bounty_hunter</tag>
+    <tag display="Celebrity" description="Famous enough that other characters could plausibily already know about them">celebrity</tag>
+    <tag display="Clown" description="A clown as an occupation">clown</tag>
+    <tag display="Criminal" description="Regularly engages in criminal activity">criminal</tag>
+    <tag display="Detective" description="Works as a detective">detective</tag>
+    <tag display="Gamer" description="Plays video games as a career or hobby">gamer</tag>
+    <tag display="Lawyer" description="Works as an attorney">lawyer</tag>
+    <tag display="Fighter" description="Regularly takes part in close-quarters combat">fighter</tag>
+    <tag display="Journalist" description="A news reporter, or perhaps might want to interview other characters">journalist</tag>
+    <tag display="Magical Girl" description="A magical girl in the Mahou Shoujo genre">magical_girl</tag>
+    <tag display="Mech Pilot" description="Regularly pilots a mech of some sort">mech_pilot</tag>
+    <tag display="Medic" description="Either a doctor or a combat medic">medic</tag>
+    <tag display="Military" description="From a military background or belongs to a military organization">military</tag>
+    <tag display="Model" description="Works, or has worked, as a fashion/glamour model">model</tag>
+    <tag display="Musician" description="Sings or plays an instrument either professionally or as a hobby">musician</tag>
+    <tag display="Ninja" description="A ninja">ninja</tag>
+    <tag display="Pirate" description="A classic, modern pirate, or space pirate">pirate</tag>
+    <tag display="Police" description="Belongs to a law enforcement organization">police</tag>
+    <tag display="Prince" description="A prince">prince</tag>
+    <tag display="Princess" description="A princess">princess</tag>
+    <tag display="Scientist" description="Any variety of scientist">scientist</tag>
+    <tag display="Superhero" description="A superhero, likely possession superpowers">superhero</tag>
+    <tag display="Spy" description="A spy or some other form of covert operative">spy</tag>
+    <tag display="Student" description="Attends a school, college, or university">student</tag>
+    <tag display="Witch" description="A witch">witch</tag>
   </group>
   <group label="Stripping/Forfeit Gimmicks" multiselect="true">
-    <tag display="Cheater">cheater</tag>
-	<tag display="Disguised as Opposite Sex">crossdresser</tag>
-    <tag display="Assisted Stripping">assisted_strip</tag>
-	<tag display="Clothing Destroyed when Stripping">clothing_destruction</tag>
-	<tag display="Shows Crotch Before Chest">bottomless_first</tag>
-    <tag display="Multi-person Forfeit">tandem</tag>
-    <tag display="Tentacle/Vine Forfeit">tentacle_forfeit</tag>
-    <tag display="Restrained During Forfeit">bondage_forfeit</tag>
-	<tag display="Uses Toy for Forfeit">uses_toy</tag>
+    <tag display="Cheater" description="Character uses underhanded means to gain an advantage in the game">cheater</tag>
+    <tag display="Disguised as Opposite Sex" description="Character is dressed like what is traditionally associated with the opposite sex">crossdresser</tag>
+    <tag display="Assisted Stripping" description="Someone other than the character removes the character's clothing">assisted_strip</tag>
+    <tag display="Clothing Destroyed when Stripping" description="Character's clothes are destroyed over the course of the game">clothing_destruction</tag>
+    <tag display="Shows Crotch Before Chest" description="Character reveals their crotch before their chest">bottomless_first</tag>
+    <tag display="Multi-person Forfeit" description="Character masturbates with a partner">tandem</tag>
+    <tag display="Tentacle/Vine Forfeit" description="Character masturbates with tentacles or vines">tentacle_forfeit</tag>
+    <tag display="Restrained During Forfeit" description="Character masturbates using bondage equipment">bondage_forfeit</tag>
+    <tag display="Uses Toy for Forfeit" description="Character uses a sex toy to masturbate">uses_toy</tag>
   </group>
   <group label="Other" multiselect="true">
-    <tag display="Creepy">creepy</tag>
-    <tag display="Drunk">drunk</tag>
-    <tag display="Flies">flies</tag>
-    <tag display="Goth">goth</tag>
-	<tag display="Has a Camera">uses_camera</tag>
-    <tag display="Magical">magic</tag>
-	<tag display="Psychic">psychic</tag>
-    <tag display="Hero">hero</tag>
-    <tag display="Villain">villain</tag>
-    <tag display="Virtual">virtual</tag>
+    <tag display="Creepy" description="Character has a creepy or unnerving appearance or personality">creepy</tag>
+    <tag display="Drunk" description="Character drinks over the course of the game">drunk</tag>
+    <tag display="Flies" description="Character visibly flies, hovers, or floats during the game">flies</tag>
+    <tag display="Goth" description="Character dresses in a gothic manner or has a stereotypical goth personality">goth</tag>
+    <tag display="Has a Camera" description="Character has a camera that they use during the game">uses_camera</tag>
+    <tag display="Magical" description="Character is capable of using magic">magic</tag>
+    <tag display="Psychic" description="Character has psychic abilities of some sort">psychic</tag>
+    <tag display="Hero" description="Character is a hero in their source material and fights for good">hero</tag>
+    <tag display="Villain" description="Character is a villain in their source material">villain</tag>
+    <tag display="Virtual" description="Character is a digital avatar, hologrma, or AI">virtual</tag>
   </group>
   <group hidden="true">
-    <tag display="Human">human</tag>
-    <tag display="Human (Male)">human_male</tag>
-    <tag display="Human (Female)">human_female</tag>
+    <tag display="Human" description="Character is the human player">human</tag>
+    <tag display="Human (Male)" description="Character is the human player as a male">human_male</tag>
+    <tag display="Human (Female)" description="Character is the human player as a female">human_female</tag>
   </group>
 </tagdictionary>
diff --git a/editor source/SPNATI Character Editor/variables.xml b/editor source/SPNATI Character Editor/variables.xml
index 537d7b35628d64c7b8d95dd47d3560d04c22778b..b030c2a4aa0dc2613ba4c7d713b39f463b4b8619 100644
--- a/editor source/SPNATI Character Editor/variables.xml	
+++ b/editor source/SPNATI Character Editor/variables.xml	
@@ -22,6 +22,7 @@
   <variable name="weekday" description="Displays the current day of the week." global="true" />
   <variable name="background" description="Displays the name of the background the player is using." global="true">
     <function name="location" description="Displays 'indoors' or 'outdoors' depending on which background is in play."/>
+    <function name="time" description="Displays 'day' or 'night' depending on which background is in play."/>
   </variable>
   <variable name="target" description="Displays the target's name, same as ~name~.">
     <function name="costume" description="Expands to the ID of the current skin the target is using, or 'default' by default." />
@@ -30,10 +31,24 @@
     <function name="size" description="Expands to small, medium, or large based on the character's chest or penis size." />
     <function name="slot" description="Expands to 1, 2, 3, or 4 based on the target's position on screen, where 1 is leftmost and 4 is rightmost." />
     <function name="tag" description="Expands to 'true' or 'false' depending on if the target has this tag." />
+    <function name="place" description="Expands to the character's current place (1-5) based on layers remaining." />
+    <function name="revplace" description="Expands to the character's current place in reverse order (1-5) based on layers remaining, so 1 means in last place." />
+    <function name="lead" description="Expands to the number of remaining layers this character has over 2nd place when in the leader, or a negative number difference from the leader if not in first." />
+    <function name="trail" description="When in last place, expands to the difference in layers between this character and 2nd to last." />
+    <function name="diff" description="Difference between the number of layers of this player and other.">
+      <param name="other" description="ID of the character to compare to." />
+    </function>
   </variable>
   <variable name="self" description="Displays the target's name, same as ~name~." global="true">
     <function name="costume" description="Expands to the ID of the current skin the character is using, or 'default' by default." />
     <function name="slot" description="Expands to 1, 2, 3, or 4 based on the character's position on screen, where 1 is leftmost and 4 is rightmost." />
     <function name="tag" description="Expands to 'true' or 'false' depending on if the character has this tag." />
+    <function name="place" description="Expands to the character's current place (1-5) based on layers remaining." />
+    <function name="revplace" description="Expands to the character's current place in reverse order (1-5) based on layers remaining, so 1 means in last place." />
+    <function name="lead" description="Expands to the number of remaining layers this character has over 2nd place when in the leader, or a negative number difference from the leader if not in first." />
+    <function name="trail" description="When in last place, expands to the difference in layers between this character and 2nd to last." />
+    <function name="diff" description="Difference between the number of layers of this player and other.">
+      <param name="other" description="ID of the character to compare to." />
+    </function>
   </variable>
 </variables>
diff --git a/editor source/UnitTests/BehaviorTests.cs b/editor source/UnitTests/BehaviorTests.cs
index dc392be8c9fb4a1dceb6d4226143df9e637774b0..e71411a1ad20a5d2b1babb0ae4180bb1180fbd80 100644
--- a/editor source/UnitTests/BehaviorTests.cs	
+++ b/editor source/UnitTests/BehaviorTests.cs	
@@ -134,7 +134,7 @@ namespace UnitTests
 		[TestMethod]
 		public void StageTreeSharedCase()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			behavior.PrepareForEdit(_character);
 			CreateWorkingCase(behavior, "a", new int[] { 1, 2, 3 }, "a1");
 			behavior.BuildStageTree(_character);
@@ -155,7 +155,7 @@ namespace UnitTests
 		[TestMethod]
 		public void StageTreeCrossStages()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			behavior.PrepareForEdit(_character);
 			CreateWorkingCase(behavior, "a", new int[] { 1, 2, 3 }, "a1");
 			CreateWorkingCase(behavior, "b", new int[] { 1, 3 }, "b1");
@@ -169,7 +169,7 @@ namespace UnitTests
 		[TestMethod]
 		public void StageTreeMergeDialogue()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			behavior.PrepareForEdit(_character);
 			CreateWorkingCase(behavior, "a", new int[] { 0 }, "a1");
 			CreateWorkingCase(behavior, "a", new int[] { 0 }, "a2");
@@ -181,7 +181,7 @@ namespace UnitTests
 		[TestMethod]
 		public void DoesNotCombineAlsoPlayingWithAlsoPlayingStage()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			behavior.PrepareForEdit(_character);
 			Case c = CreateWorkingCase(behavior, "a", new int[] { 0 }, "a1");
 			c.AlsoPlaying = "bob";
@@ -197,7 +197,7 @@ namespace UnitTests
 		[TestMethod]
 		public void StageTreeIntegration()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			behavior.PrepareForEdit(_character);
 			CreateWorkingCase(behavior, "a", new int[] { 1, 2, 3 }, "z");
 			CreateWorkingCase(behavior, "b", new int[] { 1, 3 }, "y");
@@ -223,7 +223,7 @@ namespace UnitTests
 		[TestMethod]
 		public void SplitSharedCase()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			CreateCase(behavior, 1, "a", "a1");
 			CreateCase(behavior, 2, "a", "a1");
 			CreateCase(behavior, 3, "a", "a1");
@@ -235,7 +235,7 @@ namespace UnitTests
 		[TestMethod]
 		public void SplitSharedCaseExtraLine()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			CreateCase(behavior, 1, "a", "a1", "a2");
 			CreateCase(behavior, 2, "a", "a1", "a2");
 			CreateCase(behavior, 3, "a", "a1");
@@ -246,7 +246,7 @@ namespace UnitTests
 		[TestMethod]
 		public void SplitSharedCaseSharedLines()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			CreateCase(behavior, 1, "a", "a1", "a2", "a3");
 			CreateCase(behavior, 2, "a", "a1", "a2");
 			CreateCase(behavior, 3, "a", "a1", "a2");
@@ -255,20 +255,22 @@ namespace UnitTests
 		}
 
 		[TestMethod]
-		public void TreatsConditionsAsSeparateCases()
+		public void TreatsConditionsAsAlternativeCases()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			CreateCase(behavior, 0, "a", "a1", "a2");
 			Case conditioned = CreateCase(behavior, 0, "a", "a1", "a2");
 			conditioned.Filter = "x";
 			behavior.PrepareForEdit(_character);
-			Assert.AreEqual(2, behavior.GetWorkingCases().Count());
+			Assert.AreEqual(1, behavior.GetWorkingCases().Count());
+			Case first = behavior.GetWorkingCases().First();
+			Assert.AreEqual(1, first.AlternativeConditions.Count);
 		}
 
 		[TestMethod]
 		public void SplitIntegration()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			CreateCase(behavior, 1, "a", "a1", "a2");
 			CreateCase(behavior, 1, "b", "b1");
 			CreateCase(behavior, 1, "c", "b1");
@@ -283,7 +285,7 @@ namespace UnitTests
 		[TestMethod]
 		public void BackAndForth()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			CreateCase(behavior, 1, "a", "a1", "a2");
 			CreateCase(behavior, 1, "b", "b1");
 			CreateCase(behavior, 1, "c", "b1");
@@ -308,7 +310,7 @@ namespace UnitTests
 		[TestMethod]
 		public void ReplaceReplaces()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			behavior.PrepareForEdit(_character);
 			CreateWorkingCase(behavior, "a", new int[] { 0, 1, 2 }, "a1");
 			CreateWorkingCase(behavior, "b", new int[] { 0, 1, 2 }, "c");
@@ -322,7 +324,7 @@ namespace UnitTests
 		[TestMethod]
 		public void ReplaceReplacesMultiple()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			behavior.PrepareForEdit(_character);
 			CreateWorkingCase(behavior, "a", new int[] { 0, 1, 2 }, "a1");
 			CreateWorkingCase(behavior, "b", new int[] { 0, 1, 2 }, "c");
@@ -340,7 +342,7 @@ namespace UnitTests
 		[TestMethod]
 		public void ReplaceIgnoresConditions()
 		{
-			Behaviour behavior = new Behaviour();
+			Behaviour behavior = _character.Behavior;
 			behavior.PrepareForEdit(_character);
 			CreateWorkingCase(behavior, "a", new int[] { 0, 1, 2 }, "a1");
 			Case c = CreateWorkingCase(behavior, "b", new int[] { 0, 1, 2 }, "c");
diff --git a/editor source/UnitTests/DateTests.cs b/editor source/UnitTests/DateTests.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fd00ea66f1b13c94e071dc765e46d67867e6c96f
--- /dev/null
+++ b/editor source/UnitTests/DateTests.cs	
@@ -0,0 +1,130 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SPNATI_Character_Editor.Providers;
+using System;
+
+namespace UnitTests
+{
+	[TestClass]
+	public class DateTests
+	{
+		[TestMethod]
+		public void ToTime_Future()
+		{
+			DateTime last = new DateTime(2000, 1, 1);
+			DateTime now = new DateTime(1900, 1, 1, 0, 0, 0);
+			Assert.AreEqual("In the future", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_Now()
+		{
+			DateTime last = new DateTime(2000, 1, 1);
+			DateTime now = new DateTime(2000, 1, 1);
+			Assert.AreEqual("Just now", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_UnderMinute()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 1, 1, 0, 0, 30);
+			Assert.AreEqual("Just now", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_OneMinute()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 1, 1, 0, 1, 0);
+			Assert.AreEqual("1 minute ago", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_TwoMinutes()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 1, 1, 0, 2, 0);
+			Assert.AreEqual("2 minutes ago", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_OneHour()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 1, 1, 1, 0, 0);
+			Assert.AreEqual("1 hour ago", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_TwoHours()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 1, 1, 2, 0, 0);
+			Assert.AreEqual("2 hours ago", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_OneDay()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 1, 2, 0, 0, 0);
+			Assert.AreEqual("1 day ago", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_TwoDays()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 1, 3, 0, 0, 0);
+			Assert.AreEqual("2 days ago", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_OneWeek()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 1, 8, 0, 0, 0);
+			Assert.AreEqual("1 week ago", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_TwoWeeks()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 1, 15, 0, 0, 0);
+			Assert.AreEqual("2 weeks ago", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_OneMonth()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 2, 1, 0, 0, 0);
+			Assert.AreEqual("1 month ago", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_TwoMonths()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 3, 1, 0, 0, 0);
+			Assert.AreEqual("2 months ago", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_ElevenMonths()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2000, 12, 1, 0, 0, 0);
+			Assert.AreEqual("11 months ago", CharacterProvider.GetTimeSince(last, now));
+		}
+
+		[TestMethod]
+		public void ToTime_Year()
+		{
+			DateTime last = new DateTime(2000, 1, 1, 0, 0, 0);
+			DateTime now = new DateTime(2001, 1, 1, 0, 0, 0);
+			Assert.AreEqual("Over a year ago", CharacterProvider.GetTimeSince(last, now));
+		}
+	}
+}
diff --git a/editor source/UnitTests/ResponseTests.cs b/editor source/UnitTests/ResponseTests.cs
index 06361787a6d0353a928f2a85650edd04a14b1732..1dd8f52ec05c02c820d59d9e6162b74be475523b 100644
--- a/editor source/UnitTests/ResponseTests.cs	
+++ b/editor source/UnitTests/ResponseTests.cs	
@@ -13,9 +13,13 @@ namespace UnitTests
 		private static Character _male;
 		private static Character _bifemale;
 		private static Character _bimale;
+		private static Character _femaleMajor;
+		private static Character _maleMajor;
+		private static Character _bifemaleMajor;
+		private static Character _bimaleMajor;
 
-		[ClassInitialize]
-		public static void Init(TestContext context)
+		[TestInitialize]
+		public void Init()
 		{
 			TriggerDatabase.Load();
 
@@ -23,6 +27,10 @@ namespace UnitTests
 			_male = new Character() { Gender = "male", FolderName = "male" };
 			_bifemale = new Character() { Gender = "female", FolderName = "female" };
 			_bimale = new Character() { Gender = "male", FolderName = "male" };
+			_femaleMajor = new Character() { Gender = "female", FolderName = "female" };
+			_maleMajor = new Character() { Gender = "male", FolderName = "male" };
+			_bifemaleMajor = new Character() { Gender = "female", FolderName = "female" };
+			_bimaleMajor = new Character() { Gender = "male", FolderName = "male" };
 			foreach (Character c in new Character[] { _female, _male, _bifemale, _bimale })
 			{
 				c.AddLayer(new Clothing() { Type = "extra" });
@@ -31,8 +39,18 @@ namespace UnitTests
 				c.AddLayer(new Clothing() { Type = "important", Position = "upper" });
 				c.AddLayer(new Clothing() { Type = "important", Position = "lower" });
 			}
+			foreach (Character c in new Character[] { _femaleMajor, _maleMajor, _bifemaleMajor, _bimaleMajor })
+			{
+				c.AddLayer(new Clothing() { Type = "extra" });
+				c.AddLayer(new Clothing() { Type = "minor" });
+				c.AddLayer(new Clothing() { Type = "major", Position = "both" });
+				c.AddLayer(new Clothing() { Type = "major", Position = "upper" });
+				c.AddLayer(new Clothing() { Type = "major", Position = "lower" });
+			}
 			_bimale.Metadata.CrossGender = true;
 			_bifemale.Metadata.CrossGender = true;
+			_bimaleMajor.Metadata.CrossGender = true;
+			_bifemaleMajor.Metadata.CrossGender = true;
 		}
 
 		[TestMethod]
@@ -131,6 +149,22 @@ namespace UnitTests
 			Assert.AreEqual("female_crotch_will_be_visible", c.GetResponseTag(_female, _male));
 		}
 
+		[TestMethod]
+		public void Stripping_Upper_Female_AsImportant()
+		{
+			Case c = new Case("stripping");
+			c.Stages.Add(3);
+			Assert.AreEqual("female_chest_will_be_visible", c.GetResponseTag(_femaleMajor, _male));
+		}
+
+		[TestMethod]
+		public void Stripping_Lower_Female_AsImportant()
+		{
+			Case c = new Case("stripping");
+			c.Stages.Add(4);
+			Assert.AreEqual("female_crotch_will_be_visible", c.GetResponseTag(_femaleMajor, _male));
+		}
+
 		[TestMethod]
 		public void Male_Stripping_Extra_Male()
 		{
@@ -155,6 +189,16 @@ namespace UnitTests
 			Assert.AreEqual("male_removing_major", c.GetResponseTag(_male, _female));
 		}
 
+
+		[TestMethod]
+		public void Stripping_Major_Male_Both()
+		{
+			Case c = new Case("stripping");
+			c.Stages.Add(2);
+			_male.Wardrobe[2].Position = "both";
+			Assert.AreEqual("male_removing_major", c.GetResponseTag(_male, _female));
+		}
+
 		[TestMethod]
 		public void Stripping_Upper_Male()
 		{
@@ -171,6 +215,22 @@ namespace UnitTests
 			Assert.AreEqual("male_crotch_will_be_visible", c.GetResponseTag(_male, _female));
 		}
 
+		[TestMethod]
+		public void Stripping_Upper_Male_AsImportant()
+		{
+			Case c = new Case("stripping");
+			c.Stages.Add(3);
+			Assert.AreEqual("male_chest_will_be_visible", c.GetResponseTag(_maleMajor, _female));
+		}
+
+		[TestMethod]
+		public void Stripping_Lower_Male_AsImportant()
+		{
+			Case c = new Case("stripping");
+			c.Stages.Add(4);
+			Assert.AreEqual("male_crotch_will_be_visible", c.GetResponseTag(_maleMajor, _female));
+		}
+
 		[TestMethod]
 		public void Stripped_Extra_Female()
 		{
@@ -230,6 +290,41 @@ namespace UnitTests
 			Assert.AreEqual("female_crotch_is_visible", c.GetResponseTag(_female, _male));
 		}
 
+		[TestMethod]
+		public void Stripped_Upper_Large_Female_AsImportant()
+		{
+			_femaleMajor.Size = "large";
+			Case c = new Case("stripped");
+			c.Stages.Add(4);
+			Assert.AreEqual("female_large_chest_is_visible", c.GetResponseTag(_femaleMajor, _male));
+		}
+
+		[TestMethod]
+		public void Stripped_Upper_Medium_Female_AsImportant()
+		{
+			_femaleMajor.Size = "medium";
+			Case c = new Case("stripped");
+			c.Stages.Add(4);
+			Assert.AreEqual("female_medium_chest_is_visible", c.GetResponseTag(_femaleMajor, _male));
+		}
+
+		[TestMethod]
+		public void Stripped_Upper_Female_AsImportant()
+		{
+			_femaleMajor.Size = "small";
+			Case c = new Case("stripped");
+			c.Stages.Add(4);
+			Assert.AreEqual("female_small_chest_is_visible", c.GetResponseTag(_femaleMajor, _male));
+		}
+
+		[TestMethod]
+		public void Stripped_Lower_Female_AsImportant()
+		{
+			Case c = new Case("stripped");
+			c.Stages.Add(5);
+			Assert.AreEqual("female_crotch_is_visible", c.GetResponseTag(_femaleMajor, _male));
+		}
+
 		[TestMethod]
 		public void Stripped_Extra_Male()
 		{
@@ -289,6 +384,41 @@ namespace UnitTests
 			Assert.AreEqual("male_small_crotch_is_visible", c.GetResponseTag(_male, _female));
 		}
 
+		[TestMethod]
+		public void Stripped_Upper_Male_AsImportant()
+		{
+			Case c = new Case("stripped");
+			c.Stages.Add(4);
+			Assert.AreEqual("male_chest_is_visible", c.GetResponseTag(_maleMajor, _female));
+		}
+
+		[TestMethod]
+		public void Stripped_Lower_Large_Male_AsImportant()
+		{
+			_maleMajor.Size = "large";
+			Case c = new Case("stripped");
+			c.Stages.Add(5);
+			Assert.AreEqual("male_large_crotch_is_visible", c.GetResponseTag(_maleMajor, _female));
+		}
+
+		[TestMethod]
+		public void Stripped_Lower_Medium_Male_AsImportant()
+		{
+			_maleMajor.Size = "medium";
+			Case c = new Case("stripped");
+			c.Stages.Add(5);
+			Assert.AreEqual("male_medium_crotch_is_visible", c.GetResponseTag(_maleMajor, _female));
+		}
+
+		[TestMethod]
+		public void Stripped_Lower_Small_Male_AsImportant()
+		{
+			_maleMajor.Size = "small";
+			Case c = new Case("stripped");
+			c.Stages.Add(5);
+			Assert.AreEqual("male_small_crotch_is_visible", c.GetResponseTag(_maleMajor, _female));
+		}
+
 		[TestMethod]
 		public void Stripped_Extra_Opponent()
 		{
@@ -356,6 +486,49 @@ namespace UnitTests
 			Assert.AreEqual("opponent_crotch_is_visible", c.GetResponseTag(_bimale, _female));
 		}
 
+		[TestMethod]
+		public void Stripped_Upper_Opponent_AsImportant()
+		{
+			Case c = new Case("stripped");
+			c.Stages.Add(4);
+			Assert.AreEqual("opponent_chest_is_visible", c.GetResponseTag(_bimaleMajor, _female));
+		}
+
+		[TestMethod]
+		public void Stripped_Lower_Opponent_AsImportant()
+		{
+			Case c = new Case("stripped");
+			c.Stages.Add(5);
+			Assert.AreEqual("opponent_crotch_is_visible", c.GetResponseTag(_bifemaleMajor, _male));
+		}
+
+		[TestMethod]
+		public void Stripped_Lower_Large_Opponent_AsImportant()
+		{
+			_bimaleMajor.Size = "large";
+			Case c = new Case("stripped");
+			c.Stages.Add(5);
+			Assert.AreEqual("opponent_crotch_is_visible", c.GetResponseTag(_bimaleMajor, _female));
+		}
+
+		[TestMethod]
+		public void Stripped_Lower_Medium_Opponent_AsImportant()
+		{
+			_bimaleMajor.Size = "medium";
+			Case c = new Case("stripped");
+			c.Stages.Add(5);
+			Assert.AreEqual("opponent_crotch_is_visible", c.GetResponseTag(_bimaleMajor, _female));
+		}
+
+		[TestMethod]
+		public void Stripped_Lower_Small_Opponent_AsImportant()
+		{
+			_bimaleMajor.Size = "small";
+			Case c = new Case("stripped");
+			c.Stages.Add(5);
+			Assert.AreEqual("opponent_crotch_is_visible", c.GetResponseTag(_bimaleMajor, _female));
+		}
+
 		[TestMethod]
 		public void MustMasturbateFirst_Female()
 		{
@@ -489,6 +662,21 @@ namespace UnitTests
 			Assert.AreEqual("game_over_defeat", c.GetResponseTag(_male, _female));
 		}
 
+		[TestMethod]
+		public void GameOver_Defeat()
+		{
+			Case c = new Case("game_over_defeat");
+			Assert.AreEqual("game_over_defeat", c.GetResponseTag(_male, _female));
+		}
+
+		[TestMethod]
+		public void GameOver_Defeat_TargetingWinner()
+		{
+			Case c = new Case("game_over_defeat");
+			c.Target = _female.FolderName;
+			Assert.AreEqual("game_over_victory", c.GetResponseTag(_male, _female));
+		}
+
 		[TestMethod]
 		public void Hand_Good()
 		{
diff --git a/editor source/UnitTests/UnitTests.csproj b/editor source/UnitTests/UnitTests.csproj
index 59fe9cf04a04826867a1f50b74b8aac2fa169b09..7ccccaca2cc40d79b0fdc4d7d85924d70f3a7d8e 100644
--- a/editor source/UnitTests/UnitTests.csproj	
+++ b/editor source/UnitTests/UnitTests.csproj	
@@ -52,6 +52,7 @@
   <ItemGroup>
     <Compile Include="AnalyzerTests.cs" />
     <Compile Include="BehaviorTests.cs" />
+    <Compile Include="DateTests.cs" />
     <Compile Include="ImageMetadataTests.cs" />
     <Compile Include="IntellisenseTests.cs" />
     <Compile Include="MarkerTests.cs" />
@@ -59,6 +60,10 @@
     <Compile Include="ResponseTests.cs" />
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\Desktop\Desktop.csproj">
+      <Project>{ecdf4b17-c3a6-4915-a24d-00f63c52fe14}</Project>
+      <Name>Desktop</Name>
+    </ProjectReference>
     <ProjectReference Include="..\KisekaeImporter\KisekaeImporter.csproj">
       <Project>{854f0d78-23db-4aaa-bc52-3061ebd1a95c}</Project>
       <Name>KisekaeImporter</Name>
diff --git a/editor source/readme.txt b/editor source/readme.txt
index 676a905b1e4abb1cef7a875f6044580c5d42e5b8..3484ffb1d0c6e27d8c5678216a907552730d7f14 100644
--- a/editor source/readme.txt	
+++ b/editor source/readme.txt	
@@ -5,4 +5,4 @@
 5. Once in the editor Go to Help > View Help for more detailed documentation about using the character editor.
 
 
-IMPORTANT: If you are looking at this in the GitLab repository, SPNATI Character Editor.exe is not here. Get it from https://www.reddit.com/r/spnati/comments/7071mz/character_editor_official_release/
\ No newline at end of file
+IMPORTANT: If you are looking at this in the GitLab repository, SPNATI Character Editor.exe is not here. Get it from the tools folder.
\ No newline at end of file
diff --git a/opponents/dialogue_tags.xml b/opponents/dialogue_tags.xml
index 82a39d11c943e2e45bc116542599bd6277ad721e..682ca5cff929a0c57f6cfe8557b7e0691fd2f992 100644
--- a/opponents/dialogue_tags.xml
+++ b/opponents/dialogue_tags.xml
@@ -153,7 +153,7 @@
     <defaultImage>interested</defaultImage>
     <defaultText>An opponent has stripped.</defaultText>
   </trigger>
-  <trigger tag="male_human_must_strip" optional="true" start="0" end="10" label="Must Strip (Player, Male)" hasTarget="true" description="The human male player has lost a round and must remove something" group="3" order="8" color="4">
+  <trigger tag="male_human_must_strip" optional="true" start="0" end="10" label="Must Strip (Player, Male)" hasTarget="true" description="The human male player has lost a round and must remove something" group="3" order="0" color="4">
     <vars>
       <string>name</string>
       <string>target</string>
diff --git a/opponents/tag_sort_order.txt b/opponents/tag_sort_order.txt
index 500e784d2a42ee5d804fda491bee7be2dffc15cf..949844aa36ab536bfb56b3b23eee97fc6779358c 100644
--- a/opponents/tag_sort_order.txt
+++ b/opponents/tag_sort_order.txt
@@ -16,6 +16,13 @@ must_strip_normal		2	2
 must_strip_losing		2	3
 stripping			2	4
 stripped			2	5
+must_masturbate_first		10	0
+must_masturbate			10	1
+start_masturbating		10	2
+masturbating			10	3
+heavy_masturbating		10	4
+finishing_masturbating		10	5
+finished_masturbating		10	6
 male_human_must_strip		3	0
 male_must_strip			3	1
 female_human_must_strip		3	2
@@ -67,13 +74,6 @@ female_start_masturbating	9	6
 female_masturbating		9	7
 female_heavy_masturbating	9	8
 female_finished_masturbating	9	9
-must_masturbate_first		10	0
-must_masturbate			10	1
-start_masturbating		10	2
-masturbating			10	3
-heavy_masturbating		10	4
-finishing_masturbating		10	5
-finished_masturbating		10	6
 game_over_victory		11	0	
 game_over_defeat		11	1
 global				12	0	
\ No newline at end of file