Files
BallanceTasEditor/BallanceTASEditor/Core/FileOperation.cs

438 lines
18 KiB
C#

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;
private List<uint> changedItems;
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];
}
changedItems = new List<uint>();
}
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;
changedItems.Clear();
foreach (var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) {
// backup item first
changedItems.Add(item.Value.keystates);
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;
int counter = 0;
foreach (var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) {
// restore data
item.Value.keystates = changedItems[counter];
counter++;
}
}
}
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 == 0 ? 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: -1)
// 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 == 0 && absoluteRange.end == mMem.Count - 1) {
// fully remove
newPointer = null;
newPointerIndex = -1;
} else if (absoluteRange.start == 0) {
// remove from head
newPointerIndex = 0;
newPointer = mMem.FastGetNode(mPointer, mPointerIndex, absoluteRange.end + 1);
} else {
// simple remove
newPointerIndex = absoluteRange.start - 1;
newPointer = mMem.FastGetNode(mPointer, mPointerIndex, absoluteRange.start - 1);
}
} else {
newPointer = mPointer;
newPointerIndex = mPointerIndex - absoluteRange.GetCount();
}
} else {
// not affected situation
newPointer = mPointer;
newPointerIndex = mPointerIndex;
}
// the real remove operation
foreach (var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) {
mMem.Remove(item);
removedItems.AddLast(item); // backups node;
}
// 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);
// may recovered from empty list
//if (mPointer == null) return;
// re-insert data
foreach (var item in removedItems.IterateFullReversed()) {
removedItems.Remove(item);
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 ((isAddBefore && mPointerIndex >= absolutePos) ||
(!isAddBefore && 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 bool isBlankList;
private LinkedListNode<FrameData> oldPointer;
private long oldPointerIndex;
// because insert including remove oeration(overwritten mode)
// so we need include this for code re-use
private RemoveOperation internalRemoveOper;
private const long LINKEDLIST_HEAD = -1;
private const long LINKEDLIST_TAIL = -2;
public InsertOperation(long _absolutePos, LinkedList<FrameData> _data, bool _isInsertBefore, bool _isOverwritten) : base() {
absolutePos = _absolutePos;
data = _data;
isInsertBefore = _isInsertBefore;
isOverwritten = _isOverwritten;
}
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;
// because this oper have internal oper, so we need backup data after potential remove oper
// so in there, no object need to be backuped
// if the list is blank, overwritten is invalid, just normal add them.
if (mPointer == null) {
// backups
oldPointer = mPointer;
oldPointerIndex = mPointerIndex;
addStartNode = null;
isBlankList = true;
foreach (var item in data.IterateFull()) {
mMem.AddFirst(item.Value);
}
mPointer = mMem.First;
mPointerIndex = 0;
} else {
LinkedListNode<FrameData> node = mMem.FastGetNode(mPointer, mPointerIndex, absolutePos);
// absolutePos is class member and shouldn't be changed.
// but in overwritten mode, this value need to be changed so we create a temp value in there
// to instead the fucntion of original variable
var modifiedAbsolutePos = 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
// now, try init internal remove oper if in overwritten mode
// first, we need compare the length of remained item located in mMem and the length of added item
// then construct remove oper
long remainLength;
if (isInsertBefore) remainLength = absolutePos + 1;
else remainLength = mMem.Count - absolutePos;
long dataLength = data.Count;
long expectedLength = dataLength > remainLength ? remainLength : dataLength;
long expectedPos;
if (isInsertBefore) expectedPos = absolutePos - expectedLength + 1;
else expectedPos = absolutePos + expectedLength - 1;
if (isInsertBefore)
internalRemoveOper = new RemoveOperation(new SelectionRange(expectedPos, absolutePos));
else
internalRemoveOper = new RemoveOperation(new SelectionRange(absolutePos, expectedPos));
node = isInsertBefore ? node.Next : node.Previous;
internalRemoveOper.Do(ref mMem, ref mPointer, ref mPointerIndex);
// now, we can treat it as normal insert(without overwritten)
// but with one exception: absolutePos
// we need re calc absolutePos bucause we have called remove oper
if (isInsertBefore) {
if (node == null)
modifiedAbsolutePos = LINKEDLIST_TAIL;
else
modifiedAbsolutePos = absolutePos + 1 - expectedLength;
} else {
if (node == null)
modifiedAbsolutePos = LINKEDLIST_HEAD;
else
modifiedAbsolutePos -= 1;
}
}
// backups
oldPointer = mPointer;
oldPointerIndex = mPointerIndex;
addStartNode = node;
isBlankList = false;
if (isInsertBefore) {
foreach (var item in data.IterateFull()) {
if (node == null)
mMem.AddLast(item.Value);
else
mMem.AddBefore(node, item.Value);
}
} else {
foreach (var item in data.IterateFullReversed()) {
if (node == null)
mMem.AddFirst(item.Value);
else
mMem.AddAfter(node, item.Value);
}
}
if (modifiedAbsolutePos != LINKEDLIST_TAIL && modifiedAbsolutePos != LINKEDLIST_HEAD) {
if ((isInsertBefore && mPointerIndex >= modifiedAbsolutePos) ||
(!isInsertBefore && mPointerIndex > modifiedAbsolutePos))
mPointerIndex += data.Count;
}
else if (modifiedAbsolutePos == LINKEDLIST_HEAD)
mPointerIndex += data.Count;
// remove have chance to remove entire list
// so we need restore pointer in that situation
if (mPointer == null) {
mPointer = mMem.First;
mPointerIndex = mPointer == null ? -1 : 0;
}
}
}
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 (isBlankList) {
// original state is blank
// just clear mmem is ok
mMem.Clear();
// re-set pointer
mPointer = oldPointer;
mPointerIndex = oldPointerIndex;
} 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++) {
if (addStartNode == null)
mMem.RemoveLast();
else
mMem.Remove(addStartNode.Previous);
}
} else {
for (long i = 0; i < data.Count; i++) {
if (addStartNode == null)
mMem.RemoveFirst();
else
mMem.Remove(addStartNode.Next);
}
}
// re-set pointer
mPointer = oldPointer;
mPointerIndex = oldPointerIndex;
// if we use overwrite mode, we need re-add lost data
if (isOverwritten) {
internalRemoveOper.Undo(ref mMem, ref mPointer, ref mPointerIndex);
}
}
}
}
}