try adding revocable operation(no debug)
This commit is contained in:
@ -58,6 +58,7 @@
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="Core\ClipboardUtil.cs" />
|
||||
<Compile Include="Core\FileOperation.cs" />
|
||||
<Compile Include="Core\KeyboardState.cs" />
|
||||
<Compile Include="UI\AddItem.xaml.cs">
|
||||
<DependentUpon>AddItem.xaml</DependentUpon>
|
||||
|
||||
403
Core/FileOperation.cs
Normal file
403
Core/FileOperation.cs
Normal file
@ -0,0 +1,403 @@
|
||||
using BallanceTASEditor.Core.TASStruct;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BallanceTASEditor.Core.FileOperation {
|
||||
public abstract class RevocableOperation {
|
||||
public RevocableOperation() {
|
||||
hasBeenDone = false;
|
||||
}
|
||||
|
||||
private bool hasBeenDone;
|
||||
public virtual void Do(ref LinkedList<FrameData> mMem, ref LinkedListNode<FrameData> mPointer, ref long mPointerIndex) {
|
||||
if (hasBeenDone) throw new Exception("Try to call operation.do when the operation has been done.");
|
||||
hasBeenDone = true;
|
||||
}
|
||||
|
||||
public virtual void Undo(ref LinkedList<FrameData> mMem, ref LinkedListNode<FrameData> mPointer, ref long mPointerIndex) {
|
||||
if (!hasBeenDone) throw new Exception("Try to call operation.undo when the operation has not been done.");
|
||||
hasBeenDone = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SetOperation : RevocableOperation {
|
||||
private SelectionRange field;
|
||||
private SelectionRange absoluteRange;
|
||||
private bool? isSet;
|
||||
|
||||
private uint internalOffset;
|
||||
|
||||
public SetOperation(SelectionRange _field, SelectionRange _absoluteRange, bool? _isSet) : base() {
|
||||
field = _field;
|
||||
absoluteRange = _absoluteRange;
|
||||
isSet = _isSet;
|
||||
|
||||
// calc offset first for following operation
|
||||
internalOffset = 0;
|
||||
for (int i = (int)field.start; i <= (int)field.end; i++) {
|
||||
internalOffset |= ConstValue.Mapping[(FrameDataField)i];
|
||||
}
|
||||
}
|
||||
|
||||
public override void Do(ref LinkedList<FrameData> mMem, ref LinkedListNode<FrameData> mPointer, ref long mPointerIndex) {
|
||||
base.Do(ref mMem, ref mPointer, ref mPointerIndex);
|
||||
if (mPointer == null) return;
|
||||
|
||||
foreach (var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) {
|
||||
if (isSet == null) item.Value.ReverseKeyStates(internalOffset);
|
||||
else if (isSet == true) item.Value.SetKeyStates(internalOffset);
|
||||
else if (isSet == false) item.Value.UnsetKeyStates(internalOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Undo(ref LinkedList<FrameData> mMem, ref LinkedListNode<FrameData> mPointer, ref long mPointerIndex) {
|
||||
base.Undo(ref mMem, ref mPointer, ref mPointerIndex);
|
||||
if (mPointer == null) return;
|
||||
|
||||
foreach (var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) {
|
||||
// just like do operation, but switch set and unset operation
|
||||
if (isSet == null) item.Value.ReverseKeyStates(internalOffset);
|
||||
else if (isSet == true) item.Value.UnsetKeyStates(internalOffset);
|
||||
else if (isSet == false) item.Value.SetKeyStates(internalOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RemoveOperation : RevocableOperation {
|
||||
private SelectionRange absoluteRange;
|
||||
|
||||
private LinkedList<FrameData> removedItems;
|
||||
private LinkedListNode<FrameData> oldPointer;
|
||||
private long oldPointerIndex;
|
||||
private LinkedListNode<FrameData> removeStartNode;
|
||||
|
||||
public RemoveOperation(SelectionRange _absoluteRange) : base() {
|
||||
absoluteRange = _absoluteRange;
|
||||
|
||||
removedItems = new LinkedList<FrameData>();
|
||||
}
|
||||
|
||||
public override void Do(ref LinkedList<FrameData> mMem, ref LinkedListNode<FrameData> mPointer, ref long mPointerIndex) {
|
||||
base.Do(ref mMem, ref mPointer, ref mPointerIndex);
|
||||
if (mPointer == null) return;
|
||||
|
||||
// init backups list and backups 2 data
|
||||
// and backups remove start node(ps: if it is null, mean removed from head)
|
||||
removedItems.Clear();
|
||||
oldPointer = mPointer;
|
||||
oldPointerIndex = mPointerIndex;
|
||||
removeStartNode = absoluteRange.start == 1 ? null : mMem.FastGetNode(mPointer, mPointerIndex, absoluteRange.start - 1);
|
||||
|
||||
// find proper pointer after remove first. but we do not apply it in there.
|
||||
// if state is true, it mean the deleted content is placed before pointer previously. we should consider pointer data and we should correct them.
|
||||
LinkedListNode <FrameData> newPointer;
|
||||
long newPointerIndex;
|
||||
if (mPointerIndex >= absoluteRange.start) {
|
||||
// if point within removed content, we need to shift it to the head of removed content,
|
||||
// otherwise we only need to minus index with the length of removed content.
|
||||
if (absoluteRange.Within(mPointerIndex)) {
|
||||
// this contains 3 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. but it is the tail of removed content
|
||||
// otherwise, just find the head of removed content and shift to it.
|
||||
if (absoluteRange.start == 1 && absoluteRange.end == mMem.Count) {
|
||||
// fully remove
|
||||
newPointer = null;
|
||||
newPointerIndex = 0;
|
||||
} else if (absoluteRange.start == 1) {
|
||||
// remove from head
|
||||
newPointerIndex = absoluteRange.end + 1;
|
||||
newPointer = mMem.FastGetNode(mPointer, mPointerIndex, newPointerIndex);
|
||||
} else {
|
||||
// simple remove
|
||||
newPointerIndex = absoluteRange.start - 1;
|
||||
newPointer = mMem.FastGetNode(mPointer, mPointerIndex, newPointerIndex);
|
||||
}
|
||||
} else {
|
||||
newPointer = mPointer;
|
||||
newPointerIndex = mPointerIndex - absoluteRange.GetCount() + 1;
|
||||
}
|
||||
} else {
|
||||
// not affected situation
|
||||
newPointer = mPointer;
|
||||
newPointerIndex = mPointerIndex;
|
||||
}
|
||||
|
||||
// the real remove operation
|
||||
foreach (var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) {
|
||||
removedItems.AddLast(item); // backups node first;
|
||||
mMem.Remove(item);
|
||||
}
|
||||
|
||||
// apply gotten new pointer
|
||||
mPointer = newPointer;
|
||||
mPointerIndex = newPointerIndex;
|
||||
|
||||
}
|
||||
|
||||
public override void Undo(ref LinkedList<FrameData> mMem, ref LinkedListNode<FrameData> mPointer, ref long mPointerIndex) {
|
||||
base.Undo(ref mMem, ref mPointer, ref mPointerIndex);
|
||||
if (mPointer == null) return;
|
||||
|
||||
// re-insert data
|
||||
foreach (var item in removedItems.IterateFullReversed()) {
|
||||
if (removeStartNode == null) {
|
||||
// insert at first
|
||||
mMem.AddFirst(item);
|
||||
} else {
|
||||
// insert after this node
|
||||
mMem.AddAfter(removeStartNode, item);
|
||||
}
|
||||
}
|
||||
|
||||
// reset pointer
|
||||
mPointer = oldPointer;
|
||||
mPointerIndex = oldPointerIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public class AddOperation : RevocableOperation {
|
||||
private long absolutePos;
|
||||
private long count;
|
||||
private float deltaTime;
|
||||
private bool isAddBefore;
|
||||
|
||||
private LinkedListNode<FrameData> addStartNode;
|
||||
private LinkedListNode<FrameData> oldPointer;
|
||||
private long oldPointerIndex;
|
||||
|
||||
public AddOperation(long _absolutePos, long _count, float _deltaTime, bool _isAddBefore) : base() {
|
||||
absolutePos = _absolutePos;
|
||||
count = _count;
|
||||
deltaTime = _deltaTime;
|
||||
isAddBefore = _isAddBefore;
|
||||
}
|
||||
|
||||
public override void Do(ref LinkedList<FrameData> mMem, ref LinkedListNode<FrameData> mPointer, ref long mPointerIndex) {
|
||||
base.Do(ref mMem, ref mPointer, ref mPointerIndex);
|
||||
if (count <= 0) return;
|
||||
|
||||
// backups 2 data
|
||||
oldPointer = mPointer;
|
||||
oldPointerIndex = mPointerIndex;
|
||||
|
||||
// real add operation
|
||||
if (mPointer == null) {
|
||||
// backups start pointer
|
||||
addStartNode = 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
|
||||
LinkedListNode<FrameData> node = mMem.FastGetNode(mPointer, mPointerIndex, absolutePos);
|
||||
// backups start pointer
|
||||
addStartNode = node;
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// if the items are added before pointer, the index should add with the count of added items
|
||||
// but pointer don't need to be shifted.
|
||||
if (mPointerIndex > absolutePos)
|
||||
mPointerIndex += count;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Undo(ref LinkedList<FrameData> mMem, ref LinkedListNode<FrameData> mPointer, ref long mPointerIndex) {
|
||||
base.Undo(ref mMem, ref mPointer, ref mPointerIndex);
|
||||
if (count <= 0) return;
|
||||
|
||||
if (addStartNode == null) {
|
||||
// original state is blank
|
||||
// just clear mmem is ok
|
||||
mMem.Clear();
|
||||
} else {
|
||||
if (isAddBefore) {
|
||||
for (long i = 0; i < count; i++) {
|
||||
mMem.Remove(addStartNode.Previous);
|
||||
}
|
||||
} else {
|
||||
for (long i = 0; i < count; i++) {
|
||||
mMem.Remove(addStartNode.Next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// re-set pointer
|
||||
mPointer = oldPointer;
|
||||
mPointerIndex = oldPointerIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public class InsertOperation : RevocableOperation {
|
||||
private long absolutePos;
|
||||
private LinkedList<FrameData> data;
|
||||
private bool isInsertBefore;
|
||||
private bool isOverwritten;
|
||||
|
||||
private LinkedListNode<FrameData> addStartNode;
|
||||
private LinkedList<FrameData> oldItems;
|
||||
private LinkedListNode<FrameData> oldPointer;
|
||||
private long oldPointerIndex;
|
||||
|
||||
public InsertOperation(long _absolutePos, LinkedList<FrameData> _data, bool _isInsertBefore, bool _isOverwritten) : base() {
|
||||
absolutePos = _absolutePos;
|
||||
data = _data;
|
||||
isInsertBefore = _isInsertBefore;
|
||||
isOverwritten = _isOverwritten;
|
||||
|
||||
oldItems = new LinkedList<FrameData>();
|
||||
}
|
||||
|
||||
public override void Do(ref LinkedList<FrameData> mMem, ref LinkedListNode<FrameData> mPointer, ref long mPointerIndex) {
|
||||
base.Do(ref mMem, ref mPointer, ref mPointerIndex);
|
||||
if (data.Count == 0) return;
|
||||
|
||||
// init backups list and backups 2 data
|
||||
oldItems.Clear();
|
||||
oldPointer = mPointer;
|
||||
oldPointerIndex = mPointerIndex;
|
||||
|
||||
// if the list is blank, overwritten is invalid, just normal add them.
|
||||
if (mPointer == null) {
|
||||
// backups start pointer
|
||||
addStartNode = null;
|
||||
|
||||
foreach (var item in data.IterateFull()) {
|
||||
mMem.AddFirst(item.Value);
|
||||
}
|
||||
mPointer = mMem.First;
|
||||
mPointerIndex = 0;
|
||||
} else {
|
||||
LinkedListNode<FrameData> node = mMem.FastGetNode(mPointer, mPointerIndex, absolutePos);
|
||||
// if list is not a blank list, we should consider overwritten
|
||||
// if in overwritten mode, we need to overwrite data from selected item.
|
||||
// otherwise, not in overwritten mode, just normally add them just like add operation.
|
||||
if (isOverwritten) {
|
||||
// in overwritten mode, if follwoing item is not enough to fufill the count of overwritten data
|
||||
// normally add them
|
||||
// we use delete and add method to do this
|
||||
// another protential dangerous spot is pointer can be overwritten, its dangerous,
|
||||
// so we need create a backup pos to store its relative postion and in add stage set pointer to the new data.
|
||||
|
||||
var backupsNode = isInsertBefore ? node.Next : node.Previous;
|
||||
// backups start pointer
|
||||
addStartNode = backupsNode;
|
||||
|
||||
long gottenPointerPos = -1;
|
||||
for(long i = 0; i < data.Count; i++) {
|
||||
if (node == null) break;
|
||||
if (node == mPointer) gottenPointerPos = i;
|
||||
mMem.Remove(node);
|
||||
|
||||
// backup and shift to next
|
||||
if (isInsertBefore) {
|
||||
oldItems.AddFirst(node);
|
||||
node = node.Previous;
|
||||
} else {
|
||||
oldItems.AddLast(node);
|
||||
node = node.Next;
|
||||
}
|
||||
}
|
||||
|
||||
node = backupsNode;
|
||||
if (node == null) {
|
||||
foreach (var item in data.IterateFullReversed()) {
|
||||
// add from head
|
||||
mMem.AddFirst(item);
|
||||
}
|
||||
} else {
|
||||
long counter = 0;
|
||||
if (isInsertBefore) {
|
||||
foreach (var item in data.IterateFull()) {
|
||||
mMem.AddBefore(node, item.Value);
|
||||
if (counter == gottenPointerPos)
|
||||
mPointer = node.Previous;
|
||||
counter++;
|
||||
}
|
||||
} else {
|
||||
foreach (var item in data.IterateFullReversed()) {
|
||||
mMem.AddAfter(node, item.Value);
|
||||
if (counter == gottenPointerPos)
|
||||
mPointer = node.Next;
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// backups start pointer
|
||||
addStartNode = node;
|
||||
|
||||
if (isInsertBefore) {
|
||||
foreach (var item in data.IterateFull()) {
|
||||
mMem.AddBefore(node, item.Value);
|
||||
}
|
||||
} else {
|
||||
foreach (var item in data.IterateFullReversed()) {
|
||||
mMem.AddAfter(node, item.Value);
|
||||
}
|
||||
}
|
||||
if (mPointerIndex > absolutePos)
|
||||
mPointerIndex += data.Count;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override void Undo(ref LinkedList<FrameData> mMem, ref LinkedListNode<FrameData> mPointer, ref long mPointerIndex) {
|
||||
base.Undo(ref mMem, ref mPointer, ref mPointerIndex);
|
||||
if (data.Count == 0) return;
|
||||
|
||||
if (addStartNode == null) {
|
||||
// original state is blank
|
||||
// just clear mmem is ok
|
||||
mMem.Clear();
|
||||
} else {
|
||||
// in overwrite or not in overwrite mode, we all need to remove inserted data first
|
||||
if (isInsertBefore) {
|
||||
for (long i = 0; i < data.Count; i++) {
|
||||
mMem.Remove(addStartNode.Previous);
|
||||
}
|
||||
} else {
|
||||
for (long i = 0; i < data.Count; i++) {
|
||||
mMem.Remove(addStartNode.Next);
|
||||
}
|
||||
}
|
||||
|
||||
// if we use overwrite mode, we need re-add lost data
|
||||
if (isOverwritten) {
|
||||
if (isInsertBefore) {
|
||||
foreach (var item in data.IterateFull()) {
|
||||
mMem.AddBefore(addStartNode, item);
|
||||
}
|
||||
} else {
|
||||
foreach (var item in data.IterateFullReversed()) {
|
||||
mMem.AddAfter(addStartNode, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// re-set pointer
|
||||
mPointer = oldPointer;
|
||||
mPointerIndex = oldPointerIndex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user