diff --git a/BallanceTASEditor.csproj b/BallanceTASEditor.csproj index 50f1c88..7bb7a53 100644 --- a/BallanceTASEditor.csproj +++ b/BallanceTASEditor.csproj @@ -59,6 +59,9 @@ + + AddItem.xaml + @@ -83,6 +86,10 @@ MainWindow.xaml Code + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/Core/TASFile.cs b/Core/TASFile.cs index fd44d83..a30b560 100644 --- a/Core/TASFile.cs +++ b/Core/TASFile.cs @@ -17,29 +17,32 @@ namespace BallanceTASEditor.Core { fs.Close(); fs.Dispose(); mPointer = mMem.First; + mPointerIndex = 0; } public string mFilename { get; private set; } public long mFrameCount { get { return mMem.Count; } } LinkedList mMem; LinkedListNode mPointer; + long mPointerIndex; - public void Shift(long shiftNum) { - if (mPointer == null) return; - if (shiftNum == 0) return; - var absNum = Math.Abs(shiftNum); - if (shiftNum > 0) { - for(long num = 0; num < absNum && mPointer.Next != null; num++) { - mPointer = mPointer.Next; - } - } else { - for (long num = 0; num < absNum && mPointer.Previous != null; num++) { - mPointer = mPointer.Previous; - } - } + public bool IsEmpty() { + return (mPointer == null); } - public void Get(List container, long startIndex, int count) { + public long GetPointerIndex() { + if (mPointer == null) throw new Exception("Data is not ready"); + return mPointerIndex; + } + + public void Shift(long absoluteIndex) { + if (mPointer == null) return; + + mPointer = mMem.FastGetNode(mPointer, mPointerIndex, absoluteIndex); + mPointerIndex = absoluteIndex; + } + + public void Get(List container, int count) { // no item. clean container if (mPointer == null) { for(int j = 0; j < count; j++) { @@ -50,6 +53,7 @@ namespace BallanceTASEditor.Core { // fill container var cachePointer = mPointer; + var startIndex = mPointerIndex; int i; for(i = 0; i < count && cachePointer != null; i++, startIndex++) { container[i].Reload(startIndex, cachePointer.Value); @@ -62,41 +66,102 @@ namespace BallanceTASEditor.Core { } // if isSet is null, mean flip state - public void Set(SelectionRange field, SelectionRange relativeRange, bool? isSet) { + public void Set(SelectionRange field, SelectionRange absoluteRange, bool? isSet) { if (mPointer == null) return; - var cachePointer = mPointer; uint offset = 0; for(int i = (int)field.start; i <= (int)field.end; i++) { offset |= ConstValue.Mapping[(FrameDataField)i]; } - foreach(var item in mMem.IterateWithSelectionRange(relativeRange, mPointer)) { + foreach(var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) { if (isSet == null) item.Value.ReverseKeyStates(offset); else if (isSet == true) item.Value.SetKeyStates(offset); else if (isSet == false) item.Value.UnsetKeyStates(offset); } } - public void Remove(SelectionRange relativeRange) { + public void Remove(SelectionRange absoluteRange) { if (mPointer == null) return; - mMem.RemoveWithSelectionRange(relativeRange, mPointer); - // todo: fix pointer point to invalid item + // remove + foreach(var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) { + mMem.Remove(item); + } + + // correct index data + // if state is true, it mean the deleted content is placed before pointer previously. + // so we need shift the pointer to the head of selection range. + // and we should consider 2 situations, the full delete of LinkedList and delete from head + if (mPointerIndex <= absoluteRange.end) { + var newIndex = absoluteRange.start - 1; + if (newIndex < 0) { + // this contains 2 situation + // if full delete, mPointer is null and mPointerIndex is invalid(with wrong data: 0) + // if delete from head, mPointer and mPointerIndex all are valid. + mPointer = mMem.First; + mPointerIndex = 0; + } else { + mPointer = mMem.FastGetNode(mPointer, mPointerIndex, newIndex); + mPointerIndex = newIndex; + } + } } - public void Add(long relativePos, bool isAddBefore) { - + public void Add(long absolutePos, long count, float deltaTime, bool isAddBefore) { + if (count <= 0) return; + + if (mPointer == null) { + // add into blank list, absolutePos and isAddBefore parameters are invalid + // specially process + for(long i = 0; i < count; i++) { + mMem.AddFirst(new FrameData(deltaTime, 0)); + } + mPointer = mMem.First; + mPointerIndex = 0; + } else { + // normal add + // normal add doesn't affect pointer + LinkedListNode node = mMem.FastGetNode(mPointer, mPointerIndex, absolutePos); + if (isAddBefore) { + for (long i = 0; i < count; i++) { + mMem.AddBefore(node, new FrameData(deltaTime, 0)); + } + } else { + for (long i = 0; i < count; i++) { + mMem.AddAfter(node, new FrameData(deltaTime, 0)); + } + } + } } - public void Insert(long relativePos, LinkedList data, bool isInsertBefore) { + public void Insert(long absolutePos, LinkedList data, bool isInsertBefore) { + if (data.Count == 0) return; + + // the same process route with add function + if (mPointer == null) { + foreach (var item in data.IterateFull()) { + mMem.AddFirst(item.Value); + } + mPointer = mMem.First; + mPointerIndex = 0; + } else { + LinkedListNode node = mMem.FastGetNode(mPointer, mPointerIndex, absolutePos); + if (isInsertBefore) { + foreach (var item in data.IterateFull()) { + mMem.AddBefore(node, item.Value); + } + } else { + foreach (var item in data.IterateFull()) { + mMem.AddAfter(node, item.Value); + } + } + } + } + + public void Copy(SelectionRange absoluteRange, LinkedList data) { if (mPointer == null) return; - } - - public void Copy(SelectionRange relativeRange, LinkedList data) { - if (mPointer == null) return; - - foreach (var item in mMem.IterateWithSelectionRange(relativeRange, mPointer)) { + foreach (var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) { data.AddLast(item.Value); } } diff --git a/Core/Util.cs b/Core/Util.cs index cfcae7b..737d327 100644 --- a/Core/Util.cs +++ b/Core/Util.cs @@ -28,52 +28,66 @@ namespace BallanceTASEditor.Core { } } + public static LinkedListNode FastGetNode(this LinkedList ls, LinkedListNode refNode, long refIndex, long targetIndex) { + long count = ls.Count; + if (targetIndex >= count || refIndex >= count) throw new Exception("Index is invalid!"); + var span = new StupidSortStruct[3] { + new StupidSortStruct() { type = 1, data = targetIndex }, + new StupidSortStruct() { type = 2, data = count - targetIndex }, + new StupidSortStruct() { type = 3, data = targetIndex - refIndex } + }; - public static IEnumerable> IterateWithSelectionRange(this LinkedList ls, SelectionRange relativeRange, LinkedListNode current) { - if (current == null) goto end; - - // goto header first - long counter; - var cache = current.TryShiftTo(relativeRange.start, out counter); - - while (counter <= relativeRange.end && cache != null) { - yield return cache; - cache = cache.Next; - counter++; + // sort to get the min value + StupidSortStruct tmp; + if (Math.Abs(span[0].data) < Math.Abs(span[1].data)) { + tmp = span[0]; + span[0] = span[1]; + span[1] = tmp; } - - end:; + if (Math.Abs(span[1].data) < Math.Abs(span[2].data)) { + tmp = span[1]; + span[2] = span[1]; + span[2] = tmp; + } + + LinkedListNode iterateNode; + if (span[2].type == 1) iterateNode = ls.First; + else if (span[2].type == 2) iterateNode = ls.Last; + else if (span[2].type == 3) iterateNode = refNode; + else throw new Exception("Unknow node type"); + + return iterateNode.ShiftTo(span[2].data); } - public static void RemoveWithSelectionRange(this LinkedList ls, SelectionRange relativeRange, LinkedListNode current) { - if (current == null) goto end; - + // remove safety. because it store the next node. + public static IEnumerable> IterateWithSelectionRange(this LinkedList ls, SelectionRange absoluteRange, LinkedListNode refNode, long refIndex) { // goto header first - long counter; - var cache = current.TryShiftTo(relativeRange.start, out counter); + var cache = ls.FastGetNode(refNode, refIndex, absoluteRange.start); + var counter = absoluteRange.start; LinkedListNode cacheNextNode; - while (counter <= relativeRange.end && cache != null) { + while (counter <= absoluteRange.end) { + if (cache == null) throw new Exception("Unexpected head or tail of linked list!"); cacheNextNode = cache.Next; - ls.Remove(cache); + yield return cache; cache = cacheNextNode; counter++; } - - end:; } - public static LinkedListNode TryShiftTo(this LinkedListNode node, long offset, out long realShifted) { + public static LinkedListNode ShiftTo(this LinkedListNode node, long offset) { var cache = node; - realShifted = 0; + long realShifted = 0; if (offset < 0) { - while (realShifted != offset && cache.Previous != null) { + while (realShifted != offset) { + if (cache.Previous == null) throw new Exception("Unexpected head or tail of linked list!"); cache = cache.Previous; realShifted--; } } else if (offset > 0) { - while (realShifted != offset && cache.Next != null) { + while (realShifted != offset) { + if (cache.Next == null) throw new Exception("Unexpected head or tail of linked list!"); cache = cache.Next; realShifted++; } @@ -82,4 +96,37 @@ namespace BallanceTASEditor.Core { return cache; } } + + + public struct SelectionRange { + public SelectionRange(long value1, long value2) { + if (value1 > value2) { + start = value2; + end = value1; + } else { + start = value1; + end = value2; + } + } + public long start; + public long end; + public SelectionRange GetRelative(long refer) { + var res = new SelectionRange(); + res.start = start - refer; + res.end = end - refer; + return res; + } + public bool Within(long num) { + return (num >= start && num <= end); + } + public long GetCount() { + return end - start; + } + } + + public struct StupidSortStruct { + public int type; + public long data; + } + } diff --git a/UI/AddItem.xaml b/UI/AddItem.xaml new file mode 100644 index 0000000..127a281 --- /dev/null +++ b/UI/AddItem.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +