-marker undo code moved to proper place

-undo history fixes
-feature: binding markers to input
-bugfix: TasView wasn't always refreshing when inserting/deleting frames
This commit is contained in:
SuuperW 2015-03-03 08:32:39 +00:00
parent 25e54f4390
commit e40b10a0b6
7 changed files with 163 additions and 27 deletions

View File

@ -29,6 +29,7 @@ namespace BizHawk.Client.Common
public override void Truncate(int frame)
{
bool endBatch = ChangeLog.BeginNewBatch(true);
ChangeLog.AddGeneralUndo(frame, InputLogLength - 1);
if (frame < _log.Count - 1)
@ -43,12 +44,14 @@ namespace BizHawk.Client.Common
Markers.TruncateAt(frame);
ChangeLog.SetGeneralRedo();
if (endBatch)
ChangeLog.EndBatch();
}
public override void PokeFrame(int frame, IController source)
{
ChangeLog.AddGeneralUndo(frame, frame);
base.PokeFrame(frame, source);
InvalidateAfter(frame);
@ -80,47 +83,100 @@ namespace BizHawk.Client.Common
{
var invalidateAfter = frames.Min();
bool endBatch = ChangeLog.BeginNewBatch(true);
ChangeLog.AddGeneralUndo(invalidateAfter, InputLogLength - 1);
foreach (var frame in frames.OrderByDescending(x => x)) // Removin them in reverse order allows us to remove by index;
{
_log.RemoveAt(frame);
if (BindMarkersToInput) // TODO: This is slow, is there a better way to do it?
{
int firstIndex = Markers.FindIndex(m => m.Frame >= frame);
if (firstIndex != -1)
{
for (int i = firstIndex; i < Markers.Count; i++)
{
TasMovieMarker m = Markers.ElementAt(i);
if (m.Frame == frame)
Markers.Remove(m);
else
Markers.Move(m.Frame, m.Frame - 1);
}
}
}
}
Changes = true;
InvalidateAfter(invalidateAfter);
ChangeLog.SetGeneralRedo();
if (endBatch)
ChangeLog.EndBatch();
}
}
public void RemoveFrames(int removeStart, int removeUpTo)
{
bool endBatch = ChangeLog.BeginNewBatch(true);
ChangeLog.AddGeneralUndo(removeStart, InputLogLength - 1);
for (int i = removeUpTo - 1; i >= removeStart; i--)
_log.RemoveAt(i);
if (BindMarkersToInput)
{
int firstIndex = Markers.FindIndex(m => m.Frame >= removeStart);
if (firstIndex != -1)
{
for (int i = firstIndex; i < Markers.Count; i++)
{
TasMovieMarker m = Markers.ElementAt(i);
if (m.Frame < removeUpTo)
Markers.Remove(m);
else
Markers.Move(m.Frame, m.Frame - (removeUpTo - removeStart));
}
}
}
Changes = true;
InvalidateAfter(removeStart);
ChangeLog.SetGeneralRedo();
if (endBatch)
ChangeLog.EndBatch();
}
public void InsertInput(int frame, IEnumerable<string> inputLog)
{
bool endBatch = ChangeLog.BeginNewBatch(true);
ChangeLog.AddGeneralUndo(frame, InputLogLength + inputLog.Count() - 1);
_log.InsertRange(frame, inputLog);
Changes = true;
InvalidateAfter(frame);
if (BindMarkersToInput)
{
int firstIndex = Markers.FindIndex(m => m.Frame >= frame);
if (firstIndex != -1)
{
for (int i = firstIndex; i < Markers.Count; i++)
{
TasMovieMarker m = Markers.ElementAt(i);
Markers.Move(m.Frame, m.Frame + inputLog.Count());
}
}
}
ChangeLog.SetGeneralRedo();
if (endBatch)
ChangeLog.EndBatch();
}
public void InsertInput(int frame, IEnumerable<IController> inputStates)
{
ChangeLog.AddGeneralUndo(frame, InputLogLength + inputStates.Count() - 1);
// ChangeLog is done in the InsertInput call.
var lg = LogGeneratorInstance();
var inputLog = new List<string>();
@ -131,9 +187,7 @@ namespace BizHawk.Client.Common
inputLog.Add(lg.GenerateLogEntry());
}
InsertInput(frame, inputLog);
ChangeLog.SetGeneralRedo();
InsertInput(frame, inputLog); // Sets the ChangeLog
}
public void CopyOverInput(int frame, IEnumerable<IController> inputStates)
@ -156,6 +210,7 @@ namespace BizHawk.Client.Common
public void InsertEmptyFrame(int frame, int count = 1)
{
bool endBatch = ChangeLog.BeginNewBatch(true);
ChangeLog.AddGeneralUndo(frame, InputLogLength + count - 1);
var lg = LogGeneratorInstance();
@ -166,10 +221,26 @@ namespace BizHawk.Client.Common
_log.Insert(frame, lg.EmptyEntry);
}
if (BindMarkersToInput)
{
int firstIndex = Markers.FindIndex(m => m.Frame >= frame);
if (firstIndex != -1)
{
for (int i = firstIndex; i < Markers.Count; i++)
{
TasMovieMarker m = Markers.ElementAt(i);
Markers.Move(m.Frame, m.Frame + count);
}
}
}
Changes = true;
InvalidateAfter(frame - 1);
ChangeLog.SetGeneralRedo();
if (endBatch)
ChangeLog.EndBatch();
}
public void ToggleBoolState(int frame, string buttonName)
@ -186,7 +257,7 @@ namespace BizHawk.Client.Common
InvalidateAfter(frame);
ChangeLog.AddBoolToggle(frame, buttonName, !adapter[buttonName]);
}
}
}
public void SetBoolState(int frame, string buttonName, bool val)

View File

@ -49,13 +49,24 @@ namespace BizHawk.Client.Common
/// All changes made between calling Begin and End will be one Undo.
/// If already recording in a batch, calls EndBatch.
/// </summary>
public void BeginNewBatch()
/// <param name="keepOldBatch">If set and a batch is in progress, a new batch will not be created.</param>
/// <returns>Returns true if a new batch was started; otherwise false.</returns>
public bool BeginNewBatch(bool keepOldBatch = false)
{
bool ret = true;
if (RecordingBatch)
EndBatch();
{
if (keepOldBatch)
ret = false;
else
EndBatch();
}
RecordingBatch = true;
History.Add(new List<IMovieAction>());
UndoIndex++;
return ret;
}
/// <summary>
/// Ends the current undo batch. Future changes will be one undo each.
@ -65,7 +76,10 @@ namespace BizHawk.Client.Common
{
RecordingBatch = false;
List<IMovieAction> last = History.Last();
last.Capacity = last.Count;
if (last.Count == 0) // Remove batch if it's empty.
History.RemoveAt(History.Count - 1);
else
last.Capacity = last.Count;
}
/// <summary>
@ -160,19 +174,21 @@ namespace BizHawk.Client.Common
}
// TODO: These probably aren't the best way to handle undo/redo.
private int lastGeneral;
public void AddGeneralUndo(int first, int last)
{
if (AutoRecord)
{
AddMovieAction();
History.Last().Add(new MovieAction(first, last, Movie));
lastGeneral = History.Last().Count - 1;
}
}
public void SetGeneralRedo()
{
if (AutoRecord)
{
(History.Last().Last() as MovieAction).SetRedoLog(Movie);
(History.Last()[lastGeneral] as MovieAction).SetRedoLog(Movie);
}
}
@ -225,6 +241,7 @@ namespace BizHawk.Client.Common
{ get { return LastFrame - FirstFrame + 1; } }
private List<string> oldLog;
private List<string> newLog;
private bool bindMarkers;
public MovieAction(int firstFrame, int lastFrame, TasMovie movie)
{
@ -235,6 +252,8 @@ namespace BizHawk.Client.Common
undoLength = Math.Min(lastFrame + 1, movie.InputLogLength) - firstFrame;
for (int i = 0; i < undoLength; i++)
oldLog.Add(movie.GetLogEntries()[FirstFrame + i]);
bindMarkers = movie.BindMarkersToInput;
}
public void SetRedoLog(TasMovie movie)
{
@ -247,7 +266,9 @@ namespace BizHawk.Client.Common
public void Undo(TasMovie movie)
{
bool wasRecording = movie.ChangeLog.AutoRecord;
bool wasBinding = movie.BindMarkersToInput;
movie.ChangeLog.AutoRecord = false;
movie.BindMarkersToInput = bindMarkers;
if (redoLength != length)
movie.InsertEmptyFrame(movie.InputLogLength, length - redoLength);
@ -259,11 +280,14 @@ namespace BizHawk.Client.Common
movie.RemoveFrames(FirstFrame + undoLength, movie.InputLogLength);
movie.ChangeLog.AutoRecord = wasRecording;
movie.BindMarkersToInput = bindMarkers;
}
public void Redo(TasMovie movie)
{
bool wasRecording = movie.ChangeLog.AutoRecord;
bool wasBinding = movie.BindMarkersToInput;
movie.ChangeLog.AutoRecord = false;
movie.BindMarkersToInput = bindMarkers;
if (undoLength != length)
movie.InsertEmptyFrame(movie.InputLogLength, length - undoLength);
@ -275,6 +299,7 @@ namespace BizHawk.Client.Common
movie.RemoveFrames(FirstFrame + redoLength, movie.InputLogLength);
movie.ChangeLog.AutoRecord = wasRecording;
movie.BindMarkersToInput = bindMarkers;
}
}

View File

@ -45,6 +45,8 @@ namespace BizHawk.Client.Common
Markers = new TasMovieMarkerList(this);
Markers.CollectionChanged += Markers_CollectionChanged;
Markers.Add(0, startsFromSavestate ? "Savestate" : "Power on");
BindMarkersToInput = true;
}
public TasMovie(bool startsFromSavestate = false, BackgroundWorker progressReportWorker = null)
@ -63,9 +65,12 @@ namespace BizHawk.Client.Common
Markers = new TasMovieMarkerList(this);
Markers.CollectionChanged += Markers_CollectionChanged;
Markers.Add(0, startsFromSavestate ? "Savestate" : "Power on");
BindMarkersToInput = true;
}
public TasMovieMarkerList Markers { get; set; }
public bool BindMarkersToInput { get; set; }
public bool UseInputCache { get; set; }
public override string PreferredExtension

View File

@ -107,12 +107,14 @@ namespace BizHawk.Client.Common
{
if (existingItem.Message != item.Message)
{
_movie.ChangeLog.AddMarkerChange(item, item.Frame, existingItem.Message);
existingItem.Message = item.Message;
OnListChanged(NotifyCollectionChangedAction.Replace);
}
}
else
{
_movie.ChangeLog.AddMarkerChange(item);
base.Add(item);
this.Sort((m1, m2) => m1.Frame.CompareTo(m2.Frame));
OnListChanged(NotifyCollectionChangedAction.Add);
@ -126,13 +128,18 @@ namespace BizHawk.Client.Common
public new void AddRange(IEnumerable<TasMovieMarker> collection)
{
foreach(TasMovieMarker m in collection){
bool endBatch = _movie.ChangeLog.BeginNewBatch(true);
foreach (TasMovieMarker m in collection)
{
Add(m);
}
if (endBatch)
_movie.ChangeLog.EndBatch();
}
public new void Insert(int index, TasMovieMarker item)
{
_movie.ChangeLog.AddMarkerChange(item);
base.Insert(index, item);
this.Sort((m1, m2) => m1.Frame.CompareTo(m2.Frame));
OnListChanged(NotifyCollectionChangedAction.Add);
@ -140,6 +147,12 @@ namespace BizHawk.Client.Common
public new void InsertRange(int index, IEnumerable<TasMovieMarker> collection)
{
bool endBatch = _movie.ChangeLog.BeginNewBatch(true);
foreach (TasMovieMarker m in collection)
_movie.ChangeLog.AddMarkerChange(m);
if (endBatch)
_movie.ChangeLog.EndBatch();
base.InsertRange(index, collection);
this.Sort((m1, m2) => m1.Frame.CompareTo(m2.Frame));
OnListChanged(NotifyCollectionChangedAction.Add);
@ -147,12 +160,22 @@ namespace BizHawk.Client.Common
public new void Remove(TasMovieMarker item)
{
_movie.ChangeLog.AddMarkerChange(null, item.Frame, item.Message);
base.Remove(item);
OnListChanged(NotifyCollectionChangedAction.Remove);
}
public new int RemoveAll(Predicate<TasMovieMarker> match)
{
bool endBatch = _movie.ChangeLog.BeginNewBatch(true);
foreach (TasMovieMarker m in this)
{
if (match.Invoke(m))
_movie.ChangeLog.AddMarkerChange(null, m.Frame, m.Message);
}
if (endBatch)
_movie.ChangeLog.EndBatch();
int removeCount = base.RemoveAll(match);
if (removeCount > 0)
{
@ -164,6 +187,7 @@ namespace BizHawk.Client.Common
public void Move(int fromFrame, int toFrame)
{
TasMovieMarker m = Get(fromFrame);
_movie.ChangeLog.AddMarkerChange(m, m.Frame);
Insert(0, new TasMovieMarker(toFrame, m.Message));
Remove(m);
}
@ -176,13 +200,18 @@ namespace BizHawk.Client.Common
public int TruncateAt(int startFrame)
{
int deletedCount = 0;
bool endBatch = _movie.ChangeLog.BeginNewBatch(true);
for (int i = Count - 1; i > -1; i--)
{
if(this[i].Frame >= startFrame){
if (this[i].Frame >= startFrame)
{
_movie.ChangeLog.AddMarkerChange(null, this[i].Frame, this[i].Message);
RemoveAt(i);
deletedCount++;
}
}
if (endBatch)
_movie.ChangeLog.EndBatch();
if (deletedCount > 0)
{
OnListChanged(NotifyCollectionChangedAction.Remove);

View File

@ -555,10 +555,13 @@ namespace BizHawk.Client.EmuHawk
//
// BindMarkersToInputMenuItem
//
this.BindMarkersToInputMenuItem.Enabled = false;
this.BindMarkersToInputMenuItem.Checked = true;
this.BindMarkersToInputMenuItem.CheckOnClick = true;
this.BindMarkersToInputMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
this.BindMarkersToInputMenuItem.Name = "BindMarkersToInputMenuItem";
this.BindMarkersToInputMenuItem.Size = new System.Drawing.Size(288, 22);
this.BindMarkersToInputMenuItem.Text = "Bind Markers to Input";
this.BindMarkersToInputMenuItem.Click += new System.EventHandler(this.BindMarkersToInputMenuItem_Click);
//
// EmptyNewMarkerNotesMenuItem
//
@ -771,7 +774,6 @@ namespace BizHawk.Client.EmuHawk
this.TasView.FullRowSelect = true;
this.TasView.HorizontalOrientation = false;
this.TasView.LagFramesToHide = 0;
this.TasView.LastVisibleRow = 28;
this.TasView.Location = new System.Drawing.Point(8, 27);
this.TasView.MaxCharactersInHorizontal = 1;
this.TasView.MultiSelect = false;

View File

@ -251,7 +251,7 @@ namespace BizHawk.Client.EmuHawk
if (_tasClipboard.Any())
{
var needsToRollback = !(TasView.FirstSelectedIndex > Emulator.Frame);
var needsToRollback = TasView.FirstSelectedIndex < Emulator.Frame;
CurrentTasMovie.CopyOverInput(TasView.FirstSelectedIndex.Value, _tasClipboard.Select(x => x.ControllerState));
@ -280,7 +280,7 @@ namespace BizHawk.Client.EmuHawk
if (_tasClipboard.Any())
{
var needsToRollback = !(TasView.FirstSelectedIndex > Emulator.Frame);
var needsToRollback = TasView.FirstSelectedIndex < Emulator.Frame;
CurrentTasMovie.InsertInput(TasView.FirstSelectedIndex.Value, _tasClipboard.Select(x => x.ControllerState));
@ -308,7 +308,7 @@ namespace BizHawk.Client.EmuHawk
if (TasView.SelectedRows.Any())
{
var wasPaused = GlobalWin.MainForm.EmulatorPaused;
var needsToRollback = !(TasView.FirstSelectedIndex.Value > Emulator.Frame);
var needsToRollback = TasView.FirstSelectedIndex < Emulator.Frame;
var rollBackFrame = TasView.FirstSelectedIndex.Value;
_tasClipboard.Clear();
@ -384,7 +384,7 @@ namespace BizHawk.Client.EmuHawk
if (TasView.SelectedRows.Any())
{
var wasPaused = GlobalWin.MainForm.EmulatorPaused;
var needsToRollback = TasView.FirstSelectedIndex <= Emulator.Frame;
var needsToRollback = TasView.FirstSelectedIndex < Emulator.Frame;
var rollBackFrame = TasView.FirstSelectedIndex.Value;
if (rollBackFrame >= CurrentTasMovie.InputLogLength)
{ // Cannot delete non-existant frames
@ -421,7 +421,7 @@ namespace BizHawk.Client.EmuHawk
var wasPaused = GlobalWin.MainForm.EmulatorPaused;
var framesToInsert = TasView.SelectedRows.ToList();
var insertionFrame = Math.Min(TasView.LastSelectedIndex.Value + 1, CurrentTasMovie.InputLogLength);
var needsToRollback = insertionFrame <= Emulator.Frame;
var needsToRollback = TasView.FirstSelectedIndex < Emulator.Frame;
var inputLog = framesToInsert
.Select(frame => CurrentTasMovie.GetInputLogEntry(frame))
@ -452,7 +452,7 @@ namespace BizHawk.Client.EmuHawk
{
var wasPaused = GlobalWin.MainForm.EmulatorPaused;
var insertionFrame = TasView.SelectedRows.Any() ? TasView.FirstSelectedIndex.Value : 0;
var needsToRollback = insertionFrame <= Emulator.Frame;
var needsToRollback = TasView.FirstSelectedIndex < Emulator.Frame;
CurrentTasMovie.InsertEmptyFrame(insertionFrame);
@ -478,7 +478,7 @@ namespace BizHawk.Client.EmuHawk
{
var wasPaused = GlobalWin.MainForm.EmulatorPaused;
var insertionFrame = TasView.SelectedRows.Any() ? TasView.FirstSelectedIndex.Value : 0;
var needsToRollback = insertionFrame <= Emulator.Frame;
var needsToRollback = TasView.FirstSelectedIndex < Emulator.Frame;
var framesPrompt = new FramesPrompt();
var result = framesPrompt.ShowDialog();
@ -510,7 +510,7 @@ namespace BizHawk.Client.EmuHawk
if (TasView.SelectedRows.Any())
{
var rollbackFrame = TasView.LastSelectedIndex.Value;
var needsToRollback = !(rollbackFrame > Emulator.Frame);
var needsToRollback = TasView.FirstSelectedIndex < Emulator.Frame;
CurrentTasMovie.Truncate(rollbackFrame);
MarkerControl.MarkerInputRoll.TruncateSelection(CurrentTasMovie.Markers.Count - 1);
@ -587,6 +587,11 @@ namespace BizHawk.Client.EmuHawk
TasView.InputPaintingMode = Settings.DrawInput ^= true;
}
private void BindMarkersToInputMenuItem_Click(object sender, EventArgs e)
{
CurrentTasMovie.BindMarkersToInput = BindMarkersToInputMenuItem.Checked;
}
private void EmptyNewMarkerNotesMenuItem_Click(object sender, EventArgs e)
{
Settings.EmptyMarkers ^= true;

View File

@ -760,19 +760,18 @@ namespace BizHawk.Client.EmuHawk
{
TasMovieMarker marker = new TasMovieMarker(markerFrame, message);
CurrentTasMovie.Markers.Add(marker);
CurrentTasMovie.ChangeLog.AddMarkerChange(marker);
//CurrentTasMovie.ChangeLog.AddMarkerChange(marker);
}
public void RemoveMarker(TasMovieMarker marker)
{
CurrentTasMovie.Markers.Remove(marker);
CurrentTasMovie.ChangeLog.AddMarkerChange(null, marker.Frame, marker.Message);
//CurrentTasMovie.ChangeLog.AddMarkerChange(null, marker.Frame, marker.Message);
}
public void EditMarker(TasMovieMarker marker, string message)
{
string old = marker.Message;
marker.Message = message;
CurrentTasMovie.ChangeLog.AddMarkerChange(marker, marker.Frame, old);
//CurrentTasMovie.ChangeLog.AddMarkerChange(marker, marker.Frame, old);
}
}
}