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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UI/AddItem.xaml.cs b/UI/AddItem.xaml.cs
new file mode 100644
index 0000000..907e0b8
--- /dev/null
+++ b/UI/AddItem.xaml.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace BallanceTASEditor.UI {
+ ///
+ /// AddItem.xaml 的交互逻辑
+ ///
+ public partial class AddItem : Window {
+ public AddItem() {
+ InitializeComponent();
+ }
+
+ public int Output_Count { get; private set; }
+ public float Output_DeltaTime { get; private set; }
+
+ private void funcBtn_OK(object sender, RoutedEventArgs e) {
+ int count;
+ float fps;
+ if (!int.TryParse(uiTextbox_Count.Text, out count)) return;
+ if (!float.TryParse(uiTextbox_FPS.Text, out fps)) return;
+
+ Output_Count = count;
+ Output_DeltaTime = 1000f / fps;
+
+ this.DialogResult = true;
+ }
+
+ private void funcBtn_Cancel(object sender, RoutedEventArgs e) {
+ this.DialogResult = false;
+ }
+ }
+}
diff --git a/UI/DialogUtil.cs b/UI/DialogUtil.cs
index 84c8503..b46f4b7 100644
--- a/UI/DialogUtil.cs
+++ b/UI/DialogUtil.cs
@@ -42,5 +42,18 @@ namespace BallanceTASEditor.UI {
return true;
}
+ public static bool AddItemDialog(out int count, out float deltaTime) {
+ var win = new AddItem();
+ if (!(bool)win.ShowDialog()) {
+ count = 0;
+ deltaTime = 0f;
+ return false;
+ }
+
+ count = win.Output_Count;
+ deltaTime = win.Output_DeltaTime;
+ return true;
+ }
+
}
}
diff --git a/UI/SelectionHelp.cs b/UI/SelectionHelp.cs
index 6173941..af4ed8c 100644
--- a/UI/SelectionHelp.cs
+++ b/UI/SelectionHelp.cs
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using BallanceTASEditor.Core;
namespace BallanceTASEditor.UI {
public class SelectionHelp {
@@ -127,29 +128,4 @@ namespace BallanceTASEditor.UI {
}
- 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;
- }
- }
}
diff --git a/UI/StyleConverter.cs b/UI/StyleConverter.cs
index a542e45..f3fcb18 100644
--- a/UI/StyleConverter.cs
+++ b/UI/StyleConverter.cs
@@ -3,55 +3,41 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
+using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace BallanceTASEditor.UI {
- [ValueConversion(typeof(bool), typeof(Color))]
- public class BackgroundConverter : IValueConverter {
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
+ public class AddItemConverter : IMultiValueConverter {
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
try {
- bool bl = System.Convert.ToBoolean(value);
- if (bl) return Color.FromRgb(30, 144, 255);
- else return Color.FromArgb(0, 255, 255, 255);
- } catch {
- return Color.FromArgb(0, 255, 255, 255);
+ var textCount = values[0] as string;
+ var textFps = values[1] as string;
+
+ var count = int.Parse(textCount);
+ var fps = float.Parse(textFps);
+
+ if (count <= 0 || fps <= 0) return false;
+ return true;
+ } catch {
+ return false;
}
}
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
return null;
}
}
- [ValueConversion(typeof(float), typeof(string))]
- public class FloatConverter : IValueConverter {
+ public class FPS2DeltaTimeConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
- try {
- float bl = System.Convert.ToSingle(value);
- if (bl < 0) return "";
- else return bl.ToString();
- } catch {
- return "";
- }
- }
+ var text = value as string;
+ if (text == null) return "0";
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
- return null;
- }
- }
-
- [ValueConversion(typeof(long), typeof(string))]
- public class LongConverter : IValueConverter {
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
- try {
- float bl = System.Convert.ToInt64(value);
- if (bl < 0) return "";
- else return bl.ToString();
- } catch {
- return "";
- }
+ float data;
+ if (!float.TryParse(text, out data)) return "0";
+ return (1000f / data).ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
diff --git a/UI/TASViewer.cs b/UI/TASViewer.cs
index f974e36..ffecc6b 100644
--- a/UI/TASViewer.cs
+++ b/UI/TASViewer.cs
@@ -25,7 +25,6 @@ namespace BallanceTASEditor.UI {
mSelectionHelp.SelectionChanged += funcSelectionHelp_SelectionChanged;
// init data
- mPosition = 0;
INVALID_FRAME_DATA = new FrameData(-1f, 0);
mDataSource = new List();
mListLength = 0;
@@ -73,17 +72,14 @@ namespace BallanceTASEditor.UI {
TextBlock mStatusbar;
TASFlow mDataGrid;
SelectionHelp mSelectionHelp;
- long mPosition;
int mListLength;
List mDataSource;
private void sliderValueChanged(object sender, RoutedPropertyChangedEventArgs e) {
long pos = Convert.ToInt64(Math.Floor(e.NewValue));
- long offset = pos - mPosition;
- mFile.Shift(offset);
+ mFile.Shift(pos);
- RefreshDisplay(pos);
- mPosition = pos;
+ RefreshDisplay();
}
private void updateSliderRange() {
@@ -150,11 +146,11 @@ namespace BallanceTASEditor.UI {
mListLength = newLen;
// then refresh
- RefreshDisplay(mPosition);
+ RefreshDisplay();
}
- public void RefreshDisplay(long pos) {
- mFile.Get(mDataSource, pos, mListLength);
+ public void RefreshDisplay() {
+ mFile.Get(mDataSource, mListLength);
mDataGrid.RefreshDataSources();
mDataGrid.RefreshSelectionHighlight();
}
@@ -168,11 +164,19 @@ namespace BallanceTASEditor.UI {
#region data menu
private void funcDataMenu_AddBefore(object sender, RoutedEventArgs e) {
- throw new NotImplementedException();
+ if (!DialogUtil.AddItemDialog(out int count, out float deltaTime)) return;
+
+ var pos = mSelectionHelp.GetPoint();
+ mFile.Add(pos, count, deltaTime, true);
+ RefreshDisplay();
}
private void funcDataMenu_AddAfter(object sender, RoutedEventArgs e) {
- throw new NotImplementedException();
+ if (!DialogUtil.AddItemDialog(out int count, out float deltaTime)) return;
+
+ var pos = mSelectionHelp.GetPoint();
+ mFile.Add(pos, count, deltaTime, false);
+ RefreshDisplay();
}
private void funcDataMenu_PasteBefore(object sender, RoutedEventArgs e) {
@@ -192,20 +196,20 @@ namespace BallanceTASEditor.UI {
}
private void funcDataMenu_Unset(object sender, RoutedEventArgs e) {
- mFile.Set(mSelectionHelp.GetFieldRange(), mSelectionHelp.GetRange().GetRelative(mPosition), false);
- RefreshDisplay(mPosition);
+ mFile.Set(mSelectionHelp.GetFieldRange(), mSelectionHelp.GetRange(), false);
+ RefreshDisplay();
}
private void funcDataMenu_Set(object sender, RoutedEventArgs e) {
- mFile.Set(mSelectionHelp.GetFieldRange(), mSelectionHelp.GetRange().GetRelative(mPosition), true);
- RefreshDisplay(mPosition);
+ mFile.Set(mSelectionHelp.GetFieldRange(), mSelectionHelp.GetRange(), true);
+ RefreshDisplay();
}
private void funcDataMenu_Click() {
- var data = mSelectionHelp.GetPoint() - mPosition;
+ var data = mSelectionHelp.GetPoint();
var field = (int)mSelectionHelp.GetPointField();
mFile.Set(new SelectionRange(field, field), new SelectionRange(data, data), null);
- RefreshDisplay(mPosition);
+ RefreshDisplay();
}
#endregion