diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 8b5e8d5996..e60de3f2de 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -990,6 +990,7 @@ Clear Machine Stats=Clear Machine Scores Clear USB edits=Clear USB edits Clear area=Clear area Clear clipboard=Clear clipboard +Clear timing in region=Clear timing in region CoinMode=Coin Mode CoinModeNoHome=Coin Mode CoinsPerCredit=Coins Per Credit diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index f606b841f3..557c353147 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -1051,7 +1051,15 @@ static MenuDef g_SongInformation( true, EditMode_Full, true, true, 0, NULL ) ); - +// Ugh, I don't like making this global pointer to clipboardFullTiming, but +// it's the only way to make it visible to EnabledIfClipboardTimingIsSafe for +// making sure it's safe to paste as the timing data for the Steps/Song. -Kyz +static TimingData* clipboard_full_timing= NULL; +static bool EnabledIfClipboardTimingIsSafe(); +static bool EnabledIfClipboardTimingIsSafe() +{ + return clipboard_full_timing != NULL && clipboard_full_timing->IsSafeFullTiming(); +} static MenuDef g_TimingDataInformation( "ScreenMiniMenuTimingDataInformation", MenuRowDef(ScreenEdit::beat_0_offset, @@ -1102,6 +1110,9 @@ static MenuDef g_TimingDataInformation( MenuRowDef(ScreenEdit::copy_timing_in_region, "Copy timing in region", true, EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::clear_timing_in_region, + "Clear timing in region", + true, EditMode_Full, true, true, 0, NULL), MenuRowDef(ScreenEdit::paste_timing_from_clip, "Paste timing from clipboard", true, EditMode_Full, true, true, 0, NULL), @@ -1110,7 +1121,7 @@ static MenuDef g_TimingDataInformation( true, EditMode_Full, true, true, 0, NULL ), MenuRowDef(ScreenEdit::paste_full_timing, "Paste timing data", - true, EditMode_Full, true, true, 0, NULL ), + EnabledIfClipboardTimingIsSafe, EditMode_Full, true, true, 0, NULL ), MenuRowDef(ScreenEdit::erase_step_timing, "Erase step timing", true, EditMode_Full, true, true, 0, NULL ) @@ -1502,6 +1513,7 @@ void ScreenEdit::Init() m_Clipboard.SetNumTracks( m_NoteDataEdit.GetNumTracks() ); clipboardFullTiming = GAMESTATE->m_pCurSong->m_SongTiming; // always have a backup. + clipboard_full_timing= &clipboardFullTiming; m_bHasUndo = false; m_Undo.SetNumTracks( m_NoteDataEdit.GetNumTracks() ); @@ -5877,17 +5889,21 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice break; } case shift_timing_in_region_down: - m_timing_is_being_copied= false; + m_timing_change_menu_purpose= menu_is_for_shifting; m_timing_rows_being_shitted= GetRowsFromAnswers(c, iAnswers); DisplayTimingChangeMenu(); break; case shift_timing_in_region_up: - m_timing_is_being_copied= false; + m_timing_change_menu_purpose= menu_is_for_shifting; m_timing_rows_being_shitted= -GetRowsFromAnswers(c, iAnswers); DisplayTimingChangeMenu(); break; case copy_timing_in_region: - m_timing_is_being_copied= true; + m_timing_change_menu_purpose= menu_is_for_copying; + DisplayTimingChangeMenu(); + break; + case clear_timing_in_region: + m_timing_change_menu_purpose= menu_is_for_clearing; DisplayTimingChangeMenu(); break; case paste_timing_from_clip: @@ -5900,7 +5916,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice } case paste_full_timing: { - if (GAMESTATE->m_bIsUsingStepTiming) + if(GAMESTATE->m_bIsUsingStepTiming) { GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing = clipboardFullTiming; } @@ -5972,14 +5988,19 @@ void ScreenEdit::HandleTimingDataChangeChoice(TimingDataChangeChoice choice, { end= MAX_NOTE_ROW; } - if(m_timing_is_being_copied) + switch(m_timing_change_menu_purpose) { - clipboardFullTiming.Clear(); - GetAppropriateTiming().CopyRange(begin, end, change_type, 0, clipboardFullTiming); - } - else - { - GetAppropriateTimingForUpdate().ShiftRange(begin, end, change_type, m_timing_rows_being_shitted); + case menu_is_for_copying: + clipboardFullTiming.Clear(); + GetAppropriateTiming().CopyRange(begin, end, change_type, 0, clipboardFullTiming); + break; + case menu_is_for_shifting: + GetAppropriateTimingForUpdate().ShiftRange(begin, end, change_type, m_timing_rows_being_shitted); + break; + case menu_is_for_clearing: + GetAppropriateTimingForUpdate().ClearRange(begin, end, change_type); + break; + default: break; } } diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h index 7747dc54f6..1872bc9ecf 100644 --- a/src/ScreenEdit.h +++ b/src/ScreenEdit.h @@ -268,7 +268,13 @@ protected: /** @brief Display the TimingData menu for editing song and step timing. */ void DisplayTimingMenu(); - bool m_timing_is_being_copied; // Instead of being shifted. + enum TimingChangeMenuPurpose + { + menu_is_for_copying, + menu_is_for_shifting, + menu_is_for_clearing + }; + TimingChangeMenuPurpose m_timing_change_menu_purpose; int m_timing_rows_being_shitted; // How far the timing is being shitted. void DisplayTimingChangeMenu(); @@ -610,6 +616,7 @@ public: shift_timing_in_region_down, shift_timing_in_region_up, copy_timing_in_region, + clear_timing_in_region, paste_timing_from_clip, copy_full_timing, paste_full_timing, diff --git a/src/TimingData.cpp b/src/TimingData.cpp index 0d7b24aa85..90e3d070b7 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -50,6 +50,30 @@ void TimingData::Clear() } } +bool TimingData::IsSafeFullTiming() +{ + static vector needed_segments; + if(needed_segments.empty()) + { + needed_segments.push_back(SEGMENT_BPM); + needed_segments.push_back(SEGMENT_TIME_SIG); + needed_segments.push_back(SEGMENT_TICKCOUNT); + needed_segments.push_back(SEGMENT_COMBO); + needed_segments.push_back(SEGMENT_LABEL); + needed_segments.push_back(SEGMENT_SPEED); + needed_segments.push_back(SEGMENT_SCROLL); + } + vector *segs = m_avpTimingSegments; + for(size_t s= 0; s < needed_segments.size(); ++s) + { + if(segs[needed_segments[s]].empty()) + { + return false; + } + } + return true; +} + TimingData::~TimingData() { Clear(); @@ -301,6 +325,32 @@ void TimingData::ShiftRange(int start_row, int end_row, } } +void TimingData::ClearRange(int start_row, int end_row, TimingSegmentType clear_type) +{ + FOREACH_TimingSegmentType(seg_type) + { + if(seg_type == clear_type || clear_type == TimingSegmentType_Invalid) + { + vector& segs= GetTimingSegments(seg_type); + int first_affected= GetSegmentIndexAtRow(seg_type, start_row); + int last_affected= GetSegmentIndexAtRow(seg_type, end_row); + if(first_affected == INVALID_INDEX) + { + continue; + } + for(int index= last_affected; index >= first_affected; --index) + { + int seg_row= segs[index]->GetRow(); + if(segs.size() > 1 && seg_row > 0 && seg_row >= start_row && + seg_row <= end_row) + { + EraseSegment(segs, index, segs[index]); + } + } + } + } +} + void TimingData::GetActualBPM( float &fMinBPMOut, float &fMaxBPMOut, float highest ) const { fMinBPMOut = FLT_MAX; diff --git a/src/TimingData.h b/src/TimingData.h index 3ef0b332f3..d73997b47b 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -67,6 +67,7 @@ public: void Copy( const TimingData &other ); void Clear(); + bool IsSafeFullTiming(); TimingData( const TimingData &cpy ) { Copy(cpy); } TimingData& operator=( const TimingData &cpy ) { Copy(cpy); return *this; } @@ -151,6 +152,7 @@ public: void CopyRange(int start_row, int end_row, TimingSegmentType copy_type, int dest_row, TimingData& dest) const; void ShiftRange(int start_row, int end_row, TimingSegmentType shift_type, int shift_amount); + void ClearRange(int start_row, int end_row, TimingSegmentType clear_type); /** * @brief Gets the actual BPM of the song, * while respecting a limit.