Commit a350be4

Nick committed on
Fix undo bugs (closes #8)
commit a350be4118440fa49f64b1f38b7aa9b614e96257 parent 76c4536
2 changed files +101−53
Modifiedtracker.pas +1−1
@@ -687,8 +687,8 @@ begin
687 Line(0, PB.Height, PB.Width, 0); 687 Line(0, PB.Height, PB.Width, 0);
688 end; 688 end;
689 end; 689 end;
690
690 VisualizerBuffer.Draw(PB.Canvas, 0, 0, True); 691 VisualizerBuffer.Draw(PB.Canvas, 0, 0, True);
691 //PB.Canvas.Draw(0, 0, VisualizerBuffer);
692 end; 692 end;
693 693
694 procedure TfrmTracker.PreviewInstrument(Freq: Integer; Instr: Integer; 694 procedure TfrmTracker.PreviewInstrument(Freq: Integer; Instr: Integer;
Modifiedtrackergrid.pas +100−52
@@ -37,7 +37,10 @@ type
37 Pattern: TPattern; 37 Pattern: TPattern;
38 end; 38 end;
39 39
40 TUndoRedoAction = array[0..3] of TSavedPattern; 40 TSavedPatternSet = array[0..3] of TSavedPattern;
41 TUndoRedoAction = record
42 Before, After: TSavedPatternSet;
43 end;
41 TUndoDeque = TDeque<TUndoRedoAction>; 44 TUndoDeque = TDeque<TUndoRedoAction>;
42 TRedoStack = TStack<TUndoRedoAction>; 45 TRedoStack = TStack<TUndoRedoAction>;
43 46
@@ -74,7 +77,8 @@ type
74 procedure DoPaste(var Msg: TLMessage); message LM_PASTE; 77 procedure DoPaste(var Msg: TLMessage); message LM_PASTE;
75 procedure DoCopy(var Msg: TLMessage); message LM_COPY; 78 procedure DoCopy(var Msg: TLMessage); message LM_COPY;
76 procedure DoCut(var Msg: TLMessage); message LM_CUT; 79 procedure DoCut(var Msg: TLMessage); message LM_CUT;
77 procedure SaveUndoState; 80 procedure BeginUndoAction;
81 procedure EndUndoAction;
78 82
79 procedure RenderSelectedArea; 83 procedure RenderSelectedArea;
80 procedure ClampCursors; 84 procedure ClampCursors;
@@ -115,6 +119,8 @@ type
115 DragSelCursor, DragSelOther: TSelectionPos; 119 DragSelCursor, DragSelOther: TSelectionPos;
116 DragOffsetY: Integer; 120 DragOffsetY: Integer;
117 121
122 NestedUndoCount: Integer;
123 CurrentUndoAction: TUndoRedoAction;
118 Performed: TUndoDeque; 124 Performed: TUndoDeque;
119 Recall: TRedoStack; 125 Recall: TRedoStack;
120 126
@@ -226,6 +232,7 @@ begin
226 232
227 Self.PatternMap := PatternMap; 233 Self.PatternMap := PatternMap;
228 234
235 NestedUndoCount := 0;
229 Performed := TUndoDeque.Create; 236 Performed := TUndoDeque.Create;
230 Recall := TRedoStack.Create; 237 Recall := TRedoStack.Create;
231 238
@@ -388,6 +395,8 @@ begin
388 MouseButtonDown := False; 395 MouseButtonDown := False;
389 396
390 if DraggingSelection then begin 397 if DraggingSelection then begin
398 BeginUndoAction;
399
391 Selection := GetSelection; 400 Selection := GetSelection;
392 EraseSelection; 401 EraseSelection;
393 PerformPaste(Selection, DragSelCursor); 402 PerformPaste(Selection, DragSelCursor);
@@ -395,7 +404,8 @@ begin
395 Other := DragSelOther; 404 Other := DragSelOther;
396 405
397 DraggingSelection := False; 406 DraggingSelection := False;
398 SaveUndoState; 407
408 EndUndoAction;
399 end; 409 end;
400 410
401 NormalizeCursors; 411 NormalizeCursors;
@@ -415,8 +425,7 @@ procedure TTrackerGrid.KeyDown(var Key: Word; Shift: TShiftState);
415 begin 425 begin
416 inherited KeyDown(Key, Shift); 426 inherited KeyDown(Key, Shift);
417 427
418 if Key in [VK_CONTROL, VK_SHIFT] then Exit; 428 if Key in [VK_CONTROL, VK_SHIFT, VK_DELETE] then Exit;
419 if Key in [VK_DELETE] then Exit;
420 429
421 case Key of 430 case Key of
422 VK_UP: Dec(Cursor.Y); 431 VK_UP: Dec(Cursor.Y);
@@ -448,7 +457,7 @@ begin
448 if (Cursor.Y > High(TPattern)) or (Cursor.Y < Low(TPattern)) then 457 if (Cursor.Y > High(TPattern)) or (Cursor.Y < Low(TPattern)) then
449 if Assigned(OnCursorOutOfBounds) then OnCursorOutOfBounds; 458 if Assigned(OnCursorOutOfBounds) then OnCursorOutOfBounds;
450 459
451 if not (ssShift in Shift) then 460 if Shift = [] then
452 Other := Cursor; 461 Other := Cursor;
453 462
454 ClampCursors; 463 ClampCursors;
@@ -518,6 +527,8 @@ var
518 Selection: TSelection; 527 Selection: TSelection;
519 I: Integer; 528 I: Integer;
520 begin 529 begin
530 BeginUndoAction;
531
521 Selection := GetPastedCells; 532 Selection := GetPastedCells;
522 I := Cursor.Y; 533 I := Cursor.Y;
523 while I <= High(TPattern) do begin 534 while I <= High(TPattern) do begin
@@ -527,15 +538,17 @@ begin
527 end; 538 end;
528 539
529 Invalidate; 540 Invalidate;
530 SaveUndoState; 541 EndUndoAction;
531 end; 542 end;
532 543
533 procedure TTrackerGrid.DoPaste(var Msg: TLMessage); 544 procedure TTrackerGrid.DoPaste(var Msg: TLMessage);
534 begin 545 begin
546 BeginUndoAction;
547
535 PerformPaste(GetPastedCells); 548 PerformPaste(GetPastedCells);
536 549
537 Invalidate; 550 Invalidate;
538 SaveUndoState; 551 EndUndoAction;
539 end; 552 end;
540 553
541 procedure TTrackerGrid.DoCopy(var Msg: TLMessage); 554 procedure TTrackerGrid.DoCopy(var Msg: TLMessage);
@@ -549,45 +562,66 @@ begin
549 EraseSelection; 562 EraseSelection;
550 end; 563 end;
551 564
552 procedure TTrackerGrid.SaveUndoState; 565 procedure TTrackerGrid.BeginUndoAction;
553 var 566 var
554 Actn: TUndoRedoAction;
555 I: Integer; 567 I: Integer;
556 begin 568 begin
557 // First, clear out the recall stack 569 if NestedUndoCount = 0 then begin
558 while not Recall.IsEmpty do 570 // Save the "before" to our current undo action, so that EndUndoAction
559 Recall.Pop; 571 // can save the "after" and commit it to the Performed stack.
572 CurrentUndoAction := Default(TUndoRedoAction);
573 for I := Low(Patterns) to High(Patterns) do
574 with CurrentUndoAction.Before[I] do begin
575 Pattern := Patterns[I]^;
576 PatternNumber := PatternNumbers[I];
577 end;
578 end;
560 579
561 // Create the state we need to save 580 Inc(NestedUndoCount);
562 Actn := Default(TUndoRedoAction); 581 end;
563 for I := Low(Patterns) to High(Patterns) do 582
564 with Actn[I] do begin 583 procedure TTrackerGrid.EndUndoAction;
565 Pattern := Patterns[I]^; 584 var
566 PatternNumber := PatternNumbers[I]; 585 I: Integer;
567 end; 586 begin
587 if NestedUndoCount = 1 then begin
588 // First, clear out the recall stack
589 while not Recall.IsEmpty do
590 Recall.Pop;
591
592 // Save the "after" to our current undo action
593 for I := Low(Patterns) to High(Patterns) do
594 with CurrentUndoAction.After[I] do begin
595 Pattern := Patterns[I]^;
596 PatternNumber := PatternNumbers[I];
597 end;
568 598
569 // Push it into the performed queue 599 // Commit the action to the Performed stack
570 Performed.PushFront(Actn); 600 Performed.PushFront(CurrentUndoAction);
571 while Performed.Size > UNDO_STACK_SIZE do 601
572 Performed.PopBack; 602 // Keep the stack size, at maximum, UNDO_STACK_SIZE
603 while Performed.Size > UNDO_STACK_SIZE do
604 Performed.PopBack;
605 end;
606
607 Dec(NestedUndoCount);
573 end; 608 end;
574 609
575 procedure TTrackerGrid.DoUndo; 610 procedure TTrackerGrid.DoUndo;
576 var 611 var
577 OldState, NewState: TUndoRedoAction; 612 State: TUndoRedoAction;
578 I: Integer; 613 I: Integer;
579 begin 614 begin
580 if Performed.IsEmpty then Exit; 615 if Performed.IsEmpty then Exit;
581 616
582 OldState := Performed.Front; 617 State := Performed.Front;
583 Performed.PopFront; 618 Performed.PopFront;
584 NewState := Performed.Front;
585 619
586 Recall.Push(OldState); 620 Recall.Push(State);
587 621
588 for I := Low(NewState) to High(NewState) do begin 622 for I := Low(State.Before) to High(State.Before) do begin
589 LoadPattern(I, NewState[I].PatternNumber); 623 LoadPattern(I, State.Before[I].PatternNumber);
590 Patterns[I]^ := NewState[I].Pattern; 624 Patterns[I]^ := State.Before[I].Pattern;
591 end; 625 end;
592 626
593 Invalidate; 627 Invalidate;
@@ -605,9 +639,9 @@ begin
605 639
606 Performed.PushFront(State); 640 Performed.PushFront(State);
607 641
608 for I := Low(State) to High(State) do begin 642 for I := Low(State.After) to High(State.After) do begin
609 LoadPattern(I, State[I].PatternNumber); 643 LoadPattern(I, State.After[I].PatternNumber);
610 Patterns[I]^ := State[I].Pattern; 644 Patterns[I]^ := State.After[I].Pattern;
611 end; 645 end;
612 646
613 Invalidate; 647 Invalidate;
@@ -684,6 +718,8 @@ procedure TTrackerGrid.IncrementSelection(Note, Instrument, Volume, EffectCode,
684 var 718 var
685 Pos: TSelectionPos; 719 Pos: TSelectionPos;
686 begin 720 begin
721 BeginUndoAction;
722
687 NormalizeCursors; 723 NormalizeCursors;
688 724
689 Pos := Cursor; 725 Pos := Cursor;
@@ -705,7 +741,7 @@ begin
705 end; 741 end;
706 742
707 Invalidate; 743 Invalidate;
708 SaveUndoState 744 EndUndoAction
709 end; 745 end;
710 746
711 procedure TTrackerGrid.InterpolateSelection; 747 procedure TTrackerGrid.InterpolateSelection;
@@ -714,6 +750,8 @@ var
714 Pos: TSelectionPos; 750 Pos: TSelectionPos;
715 StartCell: TCell; 751 StartCell: TCell;
716 begin 752 begin
753 BeginUndoAction;
754
717 NormalizeCursors; 755 NormalizeCursors;
718 756
719 if Cursor.Y = Other.Y then Exit; 757 if Cursor.Y = Other.Y then Exit;
@@ -740,16 +778,18 @@ begin
740 end; 778 end;
741 779
742 Invalidate; 780 Invalidate;
743 SaveUndoState 781 EndUndoAction
744 end; 782 end;
745 783
746 procedure TTrackerGrid.OpenEffectEditor; 784 procedure TTrackerGrid.OpenEffectEditor;
747 begin 785 begin
786 BeginUndoAction;
787
748 frmEffectEditor.Cell := @Patterns[Cursor.X]^[Cursor.Y]; 788 frmEffectEditor.Cell := @Patterns[Cursor.X]^[Cursor.Y];
749 frmEffectEditor.ShowModal; 789 frmEffectEditor.ShowModal;
750 790
751 Invalidate; 791 Invalidate;
752 SaveUndoState 792 EndUndoAction
753 end; 793 end;
754 794
755 procedure TTrackerGrid.EraseSelection; 795 procedure TTrackerGrid.EraseSelection;
@@ -757,6 +797,8 @@ var
757 X: TSelectionPos; 797 X: TSelectionPos;
758 R: Integer; 798 R: Integer;
759 begin 799 begin
800 BeginUndoAction;
801
760 NormalizeCursors; 802 NormalizeCursors;
761 803
762 for R := Cursor.Y to Other.Y do begin 804 for R := Cursor.Y to Other.Y do begin
@@ -769,13 +811,14 @@ begin
769 end; 811 end;
770 812
771 Invalidate; 813 Invalidate;
772 SaveUndoState; 814 EndUndoAction;
773 end; 815 end;
774 816
775 procedure TTrackerGrid.InputNote(Key: Word); 817 procedure TTrackerGrid.InputNote(Key: Word);
776 var 818 var
777 Temp: Integer; 819 Temp: Integer;
778 begin 820 begin
821 BeginUndoAction;
779 Temp := -1; 822 Temp := -1;
780 823
781 with Patterns[Cursor.X]^[Cursor.Y] do 824 with Patterns[Cursor.X]^[Cursor.Y] do
@@ -795,13 +838,14 @@ begin
795 end; 838 end;
796 839
797 Invalidate; 840 Invalidate;
798 SaveUndoState; 841 EndUndoAction;
799 end; 842 end;
800 843
801 procedure TTrackerGrid.InputInstrument(Key: Word); 844 procedure TTrackerGrid.InputInstrument(Key: Word);
802 var 845 var
803 Temp: Nibble; 846 Temp: Nibble;
804 begin 847 begin
848 BeginUndoAction;
805 with Patterns[Cursor.X]^[Cursor.Y] do begin 849 with Patterns[Cursor.X]^[Cursor.Y] do begin
806 if Key = VK_DELETE then Instrument := 0 850 if Key = VK_DELETE then Instrument := 0
807 else if KeycodeToHexNumber(Key, Temp) and InRange(Temp, 0, 9) then 851 else if KeycodeToHexNumber(Key, Temp) and InRange(Temp, 0, 9) then
@@ -809,7 +853,7 @@ begin
809 end; 853 end;
810 854
811 Invalidate; 855 Invalidate;
812 SaveUndoState; 856 EndUndoAction;
813 end; 857 end;
814 858
815 procedure TTrackerGrid.InputVolume(Key: Word); 859 procedure TTrackerGrid.InputVolume(Key: Word);
@@ -819,6 +863,7 @@ end;
819 863
820 procedure TTrackerGrid.InputEffectCode(Key: Word); 864 procedure TTrackerGrid.InputEffectCode(Key: Word);
821 begin 865 begin
866 BeginUndoAction;
822 with Patterns[Cursor.X]^[Cursor.Y] do 867 with Patterns[Cursor.X]^[Cursor.Y] do
823 if Key = VK_DELETE then begin 868 if Key = VK_DELETE then begin
824 EffectCode := 0; 869 EffectCode := 0;
@@ -827,13 +872,14 @@ begin
827 else KeycodeToHexNumber(Key, EffectCode); 872 else KeycodeToHexNumber(Key, EffectCode);
828 873
829 Invalidate; 874 Invalidate;
830 SaveUndoState; 875 EndUndoAction;
831 end; 876 end;
832 877
833 procedure TTrackerGrid.InputEffectParams(Key: Word); 878 procedure TTrackerGrid.InputEffectParams(Key: Word);
834 var 879 var
835 Temp: Nibble; 880 Temp: Nibble;
836 begin 881 begin
882 BeginUndoAction;
837 with Patterns[Cursor.X]^[Cursor.Y] do 883 with Patterns[Cursor.X]^[Cursor.Y] do
838 if Key = VK_DELETE then begin 884 if Key = VK_DELETE then begin
839 EffectCode := 0; 885 EffectCode := 0;
@@ -843,7 +889,7 @@ begin
843 EffectParams.Value := ((EffectParams.Value mod $10) * $10) + Temp; 889 EffectParams.Value := ((EffectParams.Value mod $10) * $10) + Temp;
844 890
845 Invalidate; 891 Invalidate;
846 SaveUndoState; 892 EndUndoAction;
847 end; 893 end;
848 894
849 procedure TTrackerGrid.RenderRow(Row: Integer); 895 procedure TTrackerGrid.RenderRow(Row: Integer);
@@ -1189,6 +1235,7 @@ procedure TTrackerGrid.InsertRowInPatternAtCursor(Pattern: Integer);
1189 var 1235 var
1190 I: Integer; 1236 I: Integer;
1191 begin 1237 begin
1238 BeginUndoAction;
1192 NormalizeCursors; 1239 NormalizeCursors;
1193 1240
1194 for I := High(TPattern) downto Cursor.Y do 1241 for I := High(TPattern) downto Cursor.Y do
@@ -1197,26 +1244,27 @@ begin
1197 BlankCell(Patterns[Pattern]^[Cursor.Y]); 1244 BlankCell(Patterns[Pattern]^[Cursor.Y]);
1198 1245
1199 Invalidate; 1246 Invalidate;
1200 SaveUndoState; 1247 EndUndoAction;
1201 end; 1248 end;
1202 1249
1203 procedure TTrackerGrid.InsertRowInAllAtCursor; 1250 procedure TTrackerGrid.InsertRowInAllAtCursor;
1204 var 1251 var
1205 I: Integer; 1252 I: Integer;
1206 begin 1253 begin
1207 for I := Low(Patterns) to High(Patterns) do begin 1254 BeginUndoAction;
1255
1256 for I := Low(Patterns) to High(Patterns) do
1208 InsertRowInPatternAtCursor(I); 1257 InsertRowInPatternAtCursor(I);
1209 Performed.PopFront; // hack, maybe change the way this works later
1210 end;
1211 1258
1212 Invalidate; 1259 Invalidate;
1213 SaveUndoState; 1260 EndUndoAction;
1214 end; 1261 end;
1215 1262
1216 procedure TTrackerGrid.DeleteRowInPatternAtCursor(Pattern: Integer); 1263 procedure TTrackerGrid.DeleteRowInPatternAtCursor(Pattern: Integer);
1217 var 1264 var
1218 I: Integer; 1265 I: Integer;
1219 begin 1266 begin
1267 BeginUndoAction;
1220 NormalizeCursors; 1268 NormalizeCursors;
1221 1269
1222 for I := Cursor.Y to High(TPattern)-1 do 1270 for I := Cursor.Y to High(TPattern)-1 do
@@ -1225,20 +1273,20 @@ begin
1225 BlankCell(Patterns[Pattern]^[High(TPattern)]); 1273 BlankCell(Patterns[Pattern]^[High(TPattern)]);
1226 1274
1227 Invalidate; 1275 Invalidate;
1228 SaveUndoState; 1276 EndUndoAction;
1229 end; 1277 end;
1230 1278
1231 procedure TTrackerGrid.DeleteRowInAllAtCursor; 1279 procedure TTrackerGrid.DeleteRowInAllAtCursor;
1232 var 1280 var
1233 I: Integer; 1281 I: Integer;
1234 begin 1282 begin
1235 for I := Low(Patterns) to High(Patterns) do begin 1283 BeginUndoAction;
1284
1285 for I := Low(Patterns) to High(Patterns) do
1236 DeleteRowInPatternAtCursor(I); 1286 DeleteRowInPatternAtCursor(I);
1237 Performed.PopFront; // hack, maybe change the way this works later
1238 end;
1239 1287
1240 Invalidate; 1288 Invalidate;
1241 SaveUndoState; 1289 EndUndoAction;
1242 end; 1290 end;
1243 1291
1244 procedure TTrackerGrid.SelectAll; 1292 procedure TTrackerGrid.SelectAll;