using System; using System.Collections.Generic; using System.Linq; using System.Text; using BallanceTASEditor.Core; using BallanceTASEditor.Core.TASStruct; using System.Collections.ObjectModel; using System.Windows.Controls; using System.Windows; namespace BallanceTASEditor.UI { public class TASViewer : IDisposable { public TASViewer(TASFile file, TASSlider slider, TASFlow datagrid) { mFile = file; mSlider = slider; mDataGrid = datagrid; // restore slider mSlider.UpdateRange(mFile); // init selection mSelectionHelp = new SelectionHelp(); mSelectionHelp.SelectionChanged += funcSelectionHelp_SelectionChanged; // init data INVALID_FRAME_DATA = new FrameData(-1f, 0); mDataSource = new List(); mListLength = 0; mOverwrittenPaste = false; // bind event and source mDataGrid.DataSources = mDataSource; mDataGrid.SelectionHelp = mSelectionHelp; mDataGrid.Click += funcDataMenu_Click; mSlider.ValueChanged += funcSlider_ValueChanged; } public void Dispose() { mDataGrid.DataSources = null; mDataGrid.Click -= funcDataMenu_Click; mSlider.ValueChanged -= funcSlider_ValueChanged; } //const int DATA_LIST_LENGTH = 15; FrameData INVALID_FRAME_DATA; TASFile mFile; TASSlider mSlider; TASFlow mDataGrid; SelectionHelp mSelectionHelp; int mListLength; List mDataSource; bool mOverwrittenPaste; #region self event public event Action UpdateDataUI; public event Action UpdateToolMode; public event Action UpdateSelection; private void OnUpdateDataUI() { ToolMode mode = mSelectionHelp.GetToolMode(); bool showCursorPasteAddDeleteOne = mode == ToolMode.Cursor && mSelectionHelp.IsDataPartialReady(); bool showFill = mode == ToolMode.Fill && mSelectionHelp.IsDataReady(); bool showCursorCopyDelete = mode == ToolMode.Cursor && mSelectionHelp.IsDataReady(); UpdateDataUI?.Invoke(showCursorPasteAddDeleteOne, showFill, showCursorCopyDelete); } private void OnUpdateToolMode() { UpdateToolMode?.Invoke(mSelectionHelp.GetToolMode()); } private void OnUpdateSelection() { UpdateSelection?.Invoke(mSelectionHelp); } #endregion #region process event private void funcSlider_ValueChanged(long pos) { mFile.Shift(pos); RefreshDisplay(); } private void funcSelectionHelp_SelectionChanged() { mDataGrid.RefreshSelectionHighlight(); OnUpdateDataUI(); OnUpdateSelection(); } private void funcDataMenu_Click() { var data = mSelectionHelp.GetPoint(); var field = (int)mSelectionHelp.GetPointField(); mFile.Set(new SelectionRange(field, field), new SelectionRange(data, data), null); RefreshDisplay(); } #endregion public void ChangeOverwrittenMode(bool isOverwritten) { mOverwrittenPaste = isOverwritten; } public void ChangeListLength(int newLen) { if (newLen < 5 || newLen > 200) return; int offset = newLen - mListLength; int abs = Math.Abs(offset); if (offset == 0) return; // change mDataSource first if (offset > 0) { for (int i = 0; i < abs; i++) { mDataSource.Add(new FrameDataDisplay(0, INVALID_FRAME_DATA)); } } else { mDataSource.RemoveRange(newLen, abs); } // then change viewer control mDataGrid.SetItemCount(newLen); // apply new value mListLength = newLen; // then refresh RefreshDisplay(); } public void RefreshDisplay() { mFile.Get(mDataSource, mListLength); mDataGrid.RefreshDataSources(); mDataGrid.RefreshSelectionHighlight(); } public void ChangeToolMode(ToolMode mode) { mSelectionHelp.SetMode(mode); OnUpdateToolMode(); } public int GetItemCountInPage() { return mListLength; } public void ProcessOperation(OperationEnum oper) { switch (oper) { case OperationEnum.Set: case OperationEnum.Unset: { mFile.Set(mSelectionHelp.GetFieldRange(), mSelectionHelp.GetRange(), oper == OperationEnum.Set); RefreshDisplay(); } break; case OperationEnum.Cut: { // cut is a hybrid operation, first, do copy // then delete selected item // due to copy is not affect TASFile and only delete oper affect it // so this is a revocable oper var data = new LinkedList(); mFile.Copy(mSelectionHelp.GetRange(), data); if (!ClipboardUtil.SetFrameData(data)) { MessageBox.Show("Fail to cut due to unknow reason!"); break; // if fail to cut, do not delete selected items. } // do delete mFile.Remove(mSelectionHelp.GetRange()); mSelectionHelp.Reset(); mSlider.UpdateRange(mFile); RefreshDisplay(); } break; case OperationEnum.Copy: { var data = new LinkedList(); mFile.Copy(mSelectionHelp.GetRange(), data); if (!ClipboardUtil.SetFrameData(data)) MessageBox.Show("Fail to copy due to unknow reason!"); } break; case OperationEnum.PasteAfter: case OperationEnum.PasteBefore: { var data = new LinkedList(); if (ClipboardUtil.GetFrameData(data)) { mFile.Insert(mSelectionHelp.GetPoint(), data, oper == OperationEnum.PasteBefore, mOverwrittenPaste); mSelectionHelp.Reset(); mSlider.UpdateRange(mFile); RefreshDisplay(); } else MessageBox.Show("Fail to paste due to unknow reason or blank clipboard!"); } break; case OperationEnum.Delete: { mFile.Remove(mSelectionHelp.GetRange()); mSelectionHelp.Reset(); mSlider.UpdateRange(mFile); RefreshDisplay(); } break; case OperationEnum.DeleteAfter: case OperationEnum.DeleteBefore: { var pos = mSelectionHelp.GetPoint(); if (oper == OperationEnum.DeleteBefore) pos -= 1; // delete after mean delete current selected item if (pos < 0 || pos >= mFile.mFrameCount) return; // delete before need shift selection // delete before couldn't cause empty list, so we just need to directly shift if (oper == OperationEnum.DeleteBefore) mSelectionHelp.ShiftTo(false); // also, if we use delete after and delete the tail of item list, we also need to shift pos(use `else if` to prevent double shift) else if (oper == OperationEnum.DeleteAfter && pos == mFile.mFrameCount - 1) { // but delete after may cause empty list error(delete the item within only 1 item list) // so we need prevent this situation if (mFile.mFrameCount == 1) mSelectionHelp.Reset(); //yes, reset selection to prevent error else mSelectionHelp.ShiftTo(false); //no, shift selection. } // do real operation mFile.Remove(new SelectionRange(pos, pos)); mSlider.UpdateRange(mFile); RefreshDisplay(); } break; case OperationEnum.AddAfter: case OperationEnum.AddBefore: { if (!DialogUtil.AddItemDialog(out int count, out float deltaTime)) return; var pos = mSelectionHelp.GetPoint(); mFile.Add(pos, count, deltaTime, oper == OperationEnum.AddBefore); mSelectionHelp.Reset(); mSlider.UpdateRange(mFile); RefreshDisplay(); } break; case OperationEnum.Undo: { mFile.Undo(); mSelectionHelp.Reset(); mSlider.UpdateRange(mFile); RefreshDisplay(); } break; case OperationEnum.Redo: { mFile.Redo(); mSelectionHelp.Reset(); mSlider.UpdateRange(mFile); RefreshDisplay(); } break; } } } }