add undo in editor
enforce a notes per measure cap, MAX_NOTES_PER_MEASURE
This commit is contained in:
@@ -145,6 +145,9 @@ const int MAX_NOTE_TRACKS = 16;
|
||||
* by 2, 3, and 4, to exactly represent 8th, 12th and 16th notes. */
|
||||
const int ROWS_PER_BEAT = 48;
|
||||
|
||||
/* In the editor, enforce a reasonable limit on the number of notes. */
|
||||
const int MAX_NOTES_PER_MEASURE = 50;
|
||||
|
||||
const int BEATS_PER_MEASURE = 4;
|
||||
const int ROWS_PER_MEASURE = ROWS_PER_BEAT * BEATS_PER_MEASURE;
|
||||
const int MAX_NOTE_ROW = (1<<30);
|
||||
|
||||
+107
-12
@@ -395,7 +395,8 @@ static Menu g_AreaMenu(
|
||||
MenuRow( ScreenEdit::shift_pauses_forward, "Shift pauses and BPM changes down",true, false, 0, NULL ),
|
||||
MenuRow( ScreenEdit::shift_pauses_backward, "Shift pauses and BPM changes up", true, false, 0, NULL ),
|
||||
MenuRow( ScreenEdit::convert_beat_to_pause, "Convert beats to pause", true, false, 0, NULL ),
|
||||
MenuRow( ScreenEdit::convert_pause_to_beat, "Convert pause to beats", true, false, 0, NULL )
|
||||
MenuRow( ScreenEdit::convert_pause_to_beat, "Convert pause to beats", true, false, 0, NULL ),
|
||||
MenuRow( ScreenEdit::undo, "Undo", true, true, 0, NULL )
|
||||
);
|
||||
|
||||
static Menu g_StepsInformation(
|
||||
@@ -547,6 +548,8 @@ void ScreenEdit::Init()
|
||||
|
||||
m_Clipboard.SetNumTracks( m_NoteDataEdit.GetNumTracks() );
|
||||
|
||||
m_Undo.SetNumTracks( m_NoteDataEdit.GetNumTracks() );
|
||||
|
||||
|
||||
/* XXX: Do we actually have to send real note data here, and to m_NoteFieldRecord?
|
||||
* (We load again on play/record.) */
|
||||
@@ -578,7 +581,7 @@ void ScreenEdit::Init()
|
||||
this->SortByDrawOrder();
|
||||
|
||||
|
||||
m_soundAddNote.Load( THEME->GetPathS("ScreenEdit","AddNote"), true );
|
||||
m_soundAddNote.Load( THEME->GetPathS("ScreenEdit","AddNote"), true );
|
||||
m_soundRemoveNote.Load( THEME->GetPathS("ScreenEdit","RemoveNote"), true );
|
||||
m_soundChangeLine.Load( THEME->GetPathS("ScreenEdit","line"), true );
|
||||
m_soundChangeSnap.Load( THEME->GetPathS("ScreenEdit","snap"), true );
|
||||
@@ -840,18 +843,24 @@ void ScreenEdit::InputEdit( const DeviceInput& DeviceI, const InputEventType typ
|
||||
int iHeadRow;
|
||||
if( m_NoteDataEdit.IsHoldNoteAtBeat( iCol, iSongIndex, &iHeadRow ) )
|
||||
{
|
||||
m_NoteDataEdit.SetTapNote( iCol, iHeadRow, TAP_EMPTY );
|
||||
m_soundRemoveNote.Play();
|
||||
SaveUndo();
|
||||
m_NoteDataEdit.SetTapNote( iCol, iHeadRow, TAP_EMPTY );
|
||||
// Don't CheckNumberOfNotesAndUndo. We don't want to revert any change that removes notes.
|
||||
}
|
||||
else if( m_NoteDataEdit.GetTapNote(iCol, iSongIndex).type != TapNote::empty )
|
||||
{
|
||||
m_NoteDataEdit.SetTapNote( iCol, iSongIndex, TAP_EMPTY );
|
||||
m_soundRemoveNote.Play();
|
||||
SaveUndo();
|
||||
m_NoteDataEdit.SetTapNote( iCol, iSongIndex, TAP_EMPTY );
|
||||
// Don't CheckNumberOfNotesAndUndo. We don't want to revert any change that removes notes.
|
||||
}
|
||||
else if( EditIsBeingPressed(EDIT_BUTTON_LAY_MINE) )
|
||||
{
|
||||
m_NoteDataEdit.SetTapNote(iCol, iSongIndex, TAP_ORIGINAL_MINE );
|
||||
m_soundAddNote.Play();
|
||||
SaveUndo();
|
||||
m_NoteDataEdit.SetTapNote(iCol, iSongIndex, TAP_ORIGINAL_MINE );
|
||||
CheckNumberOfNotesAndUndo();
|
||||
}
|
||||
else if( EditIsBeingPressed(EDIT_BUTTON_LAY_ATTACK) )
|
||||
{
|
||||
@@ -860,8 +869,10 @@ void ScreenEdit::InputEdit( const DeviceInput& DeviceI, const InputEventType typ
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NoteDataEdit.SetTapNote(iCol, iSongIndex, TAP_ORIGINAL_TAP );
|
||||
m_soundAddNote.Play();
|
||||
SaveUndo();
|
||||
m_NoteDataEdit.SetTapNote(iCol, iSongIndex, TAP_ORIGINAL_TAP );
|
||||
CheckNumberOfNotesAndUndo();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -954,6 +965,9 @@ void ScreenEdit::InputEdit( const DeviceInput& DeviceI, const InputEventType typ
|
||||
iStartRow = max( iStartRow, 0 );
|
||||
iEndRow = max( iEndRow, 0 );
|
||||
|
||||
// Don't SaveUndo. We want to undo the whole hold, not just the last segment
|
||||
// that the user made. Dragging the hold bigger can only absorb and remove
|
||||
// other taps, so dragging won't cause us to exceed the note limit.
|
||||
m_NoteDataEdit.AddHoldNote( iCol, iStartRow, iEndRow, TAP_ORIGINAL_HOLD_HEAD );
|
||||
}
|
||||
|
||||
@@ -1023,8 +1037,8 @@ void ScreenEdit::InputEdit( const DeviceInput& DeviceI, const InputEventType typ
|
||||
bool bAreaSelected = m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1;
|
||||
g_AreaMenu.rows[cut].bEnabled = bAreaSelected;
|
||||
g_AreaMenu.rows[copy].bEnabled = bAreaSelected;
|
||||
g_AreaMenu.rows[paste_at_current_beat].bEnabled = this->m_Clipboard.GetLastBeat() != 0;
|
||||
g_AreaMenu.rows[paste_at_begin_marker].bEnabled = this->m_Clipboard.GetLastBeat() != 0 && m_NoteFieldEdit.m_iBeginMarker!=-1;
|
||||
g_AreaMenu.rows[paste_at_current_beat].bEnabled = m_Clipboard.GetLastBeat() != 0;
|
||||
g_AreaMenu.rows[paste_at_begin_marker].bEnabled = m_Clipboard.GetLastBeat() != 0 && m_NoteFieldEdit.m_iBeginMarker!=-1;
|
||||
g_AreaMenu.rows[clear].bEnabled = bAreaSelected;
|
||||
g_AreaMenu.rows[quantize].bEnabled = bAreaSelected;
|
||||
g_AreaMenu.rows[turn].bEnabled = bAreaSelected;
|
||||
@@ -1034,6 +1048,7 @@ void ScreenEdit::InputEdit( const DeviceInput& DeviceI, const InputEventType typ
|
||||
g_AreaMenu.rows[play].bEnabled = bAreaSelected;
|
||||
g_AreaMenu.rows[record].bEnabled = bAreaSelected;
|
||||
g_AreaMenu.rows[convert_beat_to_pause].bEnabled = bAreaSelected;
|
||||
g_AreaMenu.rows[undo].bEnabled = m_Undo.GetLastBeat() != 0;
|
||||
SCREENMAN->MiniMenu( &g_AreaMenu, SM_BackFromAreaMenu );
|
||||
}
|
||||
break;
|
||||
@@ -1049,6 +1064,9 @@ void ScreenEdit::InputEdit( const DeviceInput& DeviceI, const InputEventType typ
|
||||
case EDIT_BUTTON_OPEN_NEXT_STEPS:
|
||||
case EDIT_BUTTON_OPEN_PREV_STEPS:
|
||||
{
|
||||
// don't keep undo when changing Steps
|
||||
m_Undo.ClearAll();
|
||||
|
||||
// save current steps
|
||||
Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1];
|
||||
ASSERT( pSteps );
|
||||
@@ -1413,10 +1431,14 @@ void ScreenEdit::TransitionEditMode( EditMode em )
|
||||
|
||||
if( old == MODE_RECORDING )
|
||||
{
|
||||
SaveUndo();
|
||||
|
||||
// delete old TapNotes in the range
|
||||
m_NoteDataEdit.ClearRange( m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker );
|
||||
m_NoteDataEdit.CopyRange( m_NoteDataRecord, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker, m_NoteFieldEdit.m_iBeginMarker );
|
||||
m_NoteDataRecord.ClearAll();
|
||||
|
||||
CheckNumberOfNotesAndUndo();
|
||||
}
|
||||
break;
|
||||
case MODE_RECORDING:
|
||||
@@ -1539,7 +1561,11 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM )
|
||||
g_fLastInsertAttackDurationSeconds,
|
||||
false,
|
||||
0 );
|
||||
|
||||
SaveUndo();
|
||||
m_NoteDataEdit.SetTapNote( g_iLastInsertAttackTrack, row, tn );
|
||||
CheckNumberOfNotesAndUndo();
|
||||
|
||||
GAMESTATE->RestoreSelectedOptions(); // restore the edit and playback options
|
||||
}
|
||||
else if( SM == SM_DoRevertToLastSave )
|
||||
@@ -1548,6 +1574,7 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM )
|
||||
{
|
||||
CopyFromLastSave();
|
||||
m_pSteps->GetNoteData( m_NoteDataEdit );
|
||||
m_Undo.ClearAll();
|
||||
}
|
||||
}
|
||||
else if( SM == SM_DoUpdateTextInfo )
|
||||
@@ -1951,8 +1978,46 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector<int> &iAns
|
||||
};
|
||||
}
|
||||
|
||||
void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector<int> &iAnswers )
|
||||
void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector<int> &iAnswers, bool bAllowUndo )
|
||||
{
|
||||
bool bSaveUndo = false;
|
||||
switch( c )
|
||||
{
|
||||
case cut:
|
||||
case copy:
|
||||
case play:
|
||||
case record:
|
||||
case undo:
|
||||
bSaveUndo = false;
|
||||
break;
|
||||
case paste_at_current_beat:
|
||||
case paste_at_begin_marker:
|
||||
case clear:
|
||||
case quantize:
|
||||
case turn:
|
||||
case transform:
|
||||
case alter:
|
||||
case tempo:
|
||||
case insert_and_shift:
|
||||
case delete_and_shift:
|
||||
case shift_pauses_forward:
|
||||
case shift_pauses_backward:
|
||||
case convert_beat_to_pause:
|
||||
case convert_pause_to_beat:
|
||||
bSaveUndo = true;
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
// We call HandleAreaMenuChoice recursively. Only the outermost HandleAreaMenuChoice
|
||||
// should allow Undo so that the inner calls don't also save Undo and mess up the outermost
|
||||
if( !bAllowUndo )
|
||||
bSaveUndo = false;
|
||||
|
||||
if( bSaveUndo )
|
||||
SaveUndo();
|
||||
|
||||
switch( c )
|
||||
{
|
||||
case cut:
|
||||
@@ -2296,13 +2361,17 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector<int> &iAns
|
||||
NoteDataUtil::ShiftRows( m_NoteDataEdit, BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopLength) );
|
||||
m_pSong->m_Timing.ShiftRows( BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopLength) );
|
||||
}
|
||||
// Hello and welcome to I FEEL STUPID :-)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case undo:
|
||||
Undo();
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
};
|
||||
|
||||
if( bSaveUndo )
|
||||
CheckNumberOfNotesAndUndo();
|
||||
}
|
||||
|
||||
void ScreenEdit::HandleStepsInformationChoice( StepsInformationChoice c, const vector<int> &iAnswers )
|
||||
@@ -2449,12 +2518,38 @@ void ScreenEdit::CopyToLastSave()
|
||||
|
||||
void ScreenEdit::CopyFromLastSave()
|
||||
{
|
||||
|
||||
*GAMESTATE->m_pCurSong = m_songLastSave;
|
||||
if( GAMESTATE->m_pCurSteps[PLAYER_1] )
|
||||
*GAMESTATE->m_pCurSteps[PLAYER_1] = m_stepsLastSave;
|
||||
}
|
||||
|
||||
void ScreenEdit::SaveUndo()
|
||||
{
|
||||
m_Undo.CopyAll( m_NoteDataEdit );
|
||||
}
|
||||
|
||||
void ScreenEdit::Undo()
|
||||
{
|
||||
m_NoteDataEdit.CopyAll( m_Undo );
|
||||
m_Undo.ClearAll();
|
||||
}
|
||||
|
||||
void ScreenEdit::CheckNumberOfNotesAndUndo()
|
||||
{
|
||||
for( int row=0; row<=m_NoteDataEdit.GetLastRow(); row+=ROWS_PER_MEASURE )
|
||||
{
|
||||
int iNumNotesThisMeasure = 0;
|
||||
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( m_NoteDataEdit, r, row, row+ROWS_PER_MEASURE )
|
||||
iNumNotesThisMeasure += m_NoteDataEdit.GetNumTapNonEmptyTracks( r );
|
||||
if( iNumNotesThisMeasure > MAX_NOTES_PER_MEASURE )
|
||||
{
|
||||
Undo();
|
||||
SCREENMAN->Prompt( SM_None, "This change creates more than 50 notes in a measure. The change has been reverted." );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2001-2004 Chris Danford
|
||||
* All rights reserved.
|
||||
|
||||
@@ -144,6 +144,14 @@ protected:
|
||||
void PlayTicks();
|
||||
void PlayPreviewMusic();
|
||||
void UpdateTextInfo();
|
||||
|
||||
// Call this before modifying m_NoteDataEdit.
|
||||
void SaveUndo();
|
||||
// Revert m_NoteDataEdit using m_Undo.
|
||||
void Undo();
|
||||
// Call this after modifying m_NoteDataEdit. It will Undo() if
|
||||
// MAX_NOTES_PER_MEASURE was exceeded.
|
||||
void CheckNumberOfNotesAndUndo();
|
||||
|
||||
void OnSnapModeChange();
|
||||
void MenuItemGainFocus( BitmapText* menuitem );
|
||||
@@ -170,6 +178,7 @@ protected:
|
||||
int g_iShiftAnchor;
|
||||
|
||||
NoteData m_Clipboard;
|
||||
NoteData m_Undo;
|
||||
|
||||
RageSound m_soundAddNote;
|
||||
RageSound m_soundRemoveNote;
|
||||
@@ -249,10 +258,11 @@ public:
|
||||
shift_pauses_backward,
|
||||
convert_beat_to_pause,
|
||||
convert_pause_to_beat,
|
||||
undo,
|
||||
NUM_AREA_MENU_CHOICES
|
||||
};
|
||||
void HandleAreaMenuChoice( AreaMenuChoice c, const vector<int> &iAnswers );
|
||||
void HandleAreaMenuChoice( AreaMenuChoice c ) { const vector<int> v; HandleAreaMenuChoice( c, v ); }
|
||||
void HandleAreaMenuChoice( AreaMenuChoice c, const vector<int> &iAnswers, bool bAllowUndo = true );
|
||||
void HandleAreaMenuChoice( AreaMenuChoice c, bool bAllowUndo = true ) { const vector<int> v; HandleAreaMenuChoice( c, v, bAllowUndo ); }
|
||||
enum TurnType
|
||||
{
|
||||
left,
|
||||
|
||||
Reference in New Issue
Block a user