From 9e5081476f557c73a8c6b3e2e48635af0ef42d86 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Sat, 4 Apr 2015 09:10:00 -0600 Subject: [PATCH] Added timing menu options to Edit Mode that allow shifting and copying timing segments of a chosen type in the selected region or everywhere below the cursor. --- Docs/Changelog_language.txt | 40 ++++++- Themes/_fallback/Languages/en.ini | 16 +++ Themes/_fallback/metrics.ini | 3 + src/ScreenEdit.cpp | 167 ++++++++++++++++++++++++++++-- src/ScreenEdit.h | 30 +++++- src/ScreenTextEntry.cpp | 10 ++ src/ScreenTextEntry.h | 1 + src/TimingData.cpp | 71 ++++++++++++- src/TimingData.h | 1 + 9 files changed, 321 insertions(+), 18 deletions(-) diff --git a/Docs/Changelog_language.txt b/Docs/Changelog_language.txt index 97c437a8f0..4de4c315ae 100644 --- a/Docs/Changelog_language.txt +++ b/Docs/Changelog_language.txt @@ -5,13 +5,39 @@ StepMania 5.0.6. This is to inform translators of strings that need to be translated from English to other languages. ________________________________________________________________________________ -Strings are listed by the section of the language file they are in. Example: +Strings are listed by the section of the language file they are in. +One string is listed per line, to minimize confusion. +Example: -* [ScreenDebugOverlay] Mute actions, Mute actions on, Mute actions off +* [ScreenDebugOverlay] Mute actions + Mute actions on + Mute actions off This means that three strings were added to the "ScreenDebugOverlay" section, "Mute actions", "Mute actions on", and "Mute actions off". +Day not found +---------- +* [OptionTitles] All timing + BPM changes + Combo segments + Delays + Labels + Paste timing from clipboard + Scroll segments + Shift timing in region down + Shift timing in region up + Speed segments + Stops + Tickcount + Time Signatures + Warps +* [ScreenTextEntry] "%s" is an invalid integer value. + +================================================================================ +StepMania 5.0.7 | 20150401 +-------------------------------------------------------------------------------- + 2015/03/27 ---------- * [OptionExplanations] AllowSongDeletion @@ -20,8 +46,10 @@ This means that three strings were added to the "ScreenDebugOverlay" section, 2015/04/08 ---------- -* [OptionExplanations] EditClearPromptThreshold, HarshHotLifePenalty -* [OptionTitles] EditClearPromptThreshold, HarshHotLifePenalty +* [OptionExplanations] EditClearPromptThreshold + HarshHotLifePenalty +* [OptionTitles] EditClearPromptThreshold + HarshHotLifePenalty * [ScreenEdit] Are you sure you want to clear %d notes? 2015/04/02 @@ -38,7 +66,9 @@ This means that three strings were added to the "ScreenDebugOverlay" section, 2015/02/27 ---------- -* [ScreenDebugOverlay] Mute actions, Mute actions on, Mute actions off +* [ScreenDebugOverlay] Mute actions + Mute actions on + Mute actions off 2015/02/22 ---------- diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index a565c911e7..ca40353005 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -934,6 +934,7 @@ Add/Edit Background Change=Add/Edit Background Change Add Attack=Add Attack Add Mod=Add Mod Air=Air +All timing=All timing AllowExtraStage=Allow Extra Stage AllowSongDeletion=Allow Song Deletion AllowW1=Fantastic Timing @@ -962,6 +963,7 @@ BatLives=Bat Lives Base Difficulty=Base Difficulty Beat 0 Offset=Beat 0 Offset Bookkeeping=Bookkeeping +BPM changes=BPM changes Calibrate Machine Sync=Calibrate Audio Sync CPUSkill=CPU Skill CelShadeModels=Cel-shaded Models @@ -981,6 +983,7 @@ CoinMode=Coin Mode CoinModeNoHome=Coin Mode CoinsPerCredit=Coins Per Credit ComboContinuesBetweenSongs=Carry Combo +Combo segments=Combo segments Connection=Connection Convert pause to beats=Convert stop to beats Convert delay to beats=Convert delay to beats @@ -991,6 +994,7 @@ Convert selection to warp=Convert selection to Warp Segment Convert selection to fake=Convert selection to Fake Segment Copy=Copy Copy timing data=Copy timing data +Copy timing in region=Copy timing in region Copy Partial Timing=Copy Selected Timing Data CourseSortOrder=Course Sort Create New=Create New @@ -1004,6 +1008,7 @@ DefaultFailTypeNoOff=Default Fail Type DefaultNoteSkin=Default NoteSkin DelayedBack=Back Delayed DelayedTextureDelete=Keep Textures In Memory +Delays=Delays Delete=Delete Delete beat and shift up=Delete beat and shift up Description=Description @@ -1100,6 +1105,7 @@ Invalid=Invalid Invert notes' player=Invert player notes (routine only) Jumps=Jumps Key Joy Mappings=Config Key/Joy Mappings +Labels=Labels Language=Language Last second hint=Last second hint Layer=Layer @@ -1141,6 +1147,7 @@ OsMountPlayer2=OS Mount Player2 Paste at begin marker=Paste at begin marker Paste at current beat=Paste at current beat Paste timing data=Paste timing data +Paste timing from clipboard=Paste timing from clipboard Paste Partial Timing at current beat=Paste Partial Timing at current beat Persp=Perspective Play current beat to end=Play current beat to end @@ -1192,6 +1199,7 @@ Scoreboard=Scoreboard ScoringType=Scoring Type ScreenFilter=Screen Filter Scroll=Scroll +Scroll segments=Scroll segments Secret=Secret Secs Remaining=Seconds Remaining Server=Server @@ -1205,6 +1213,8 @@ Set selection start=Set selection start Set selection end=Set selection end Shift all timing changes down=Shift all timing changes down Shift all timing changes up=Shift all timing changes up +Shift timing in region down=Shift timing in region down +Shift timing in region up=Shift timing in region up ShowBanners=Show Banners ShowBeginnerHelper=Beginner Helper ShowCaution=Caution @@ -1218,8 +1228,10 @@ ShowSongOptions=Show Song Options ShowStats=Show Stats SmoothLines=SmoothLines SoundEffect=Sound Effect +Speed segments=Speed segments Starting Time=Starting Time StepMania Credits=StepMania Credits +Stops=Stops Center1Player=Center 1 Player Song=Song Song Group=Song Group @@ -1247,7 +1259,9 @@ Test Lights=Test Lights TextureColorDepth=Texture Color Theme=Theme ThreeKeyNavigation=Menu Navigation +Tickcount=Tickcount TimingWindowScale=Judge Difficulty +Time Signatures=Time Signatures Transform=Transform Transfer Edits to USB=Transfer Edits to USB Transfer Edits from USB=Transfer Edits from USB @@ -1259,6 +1273,7 @@ View steps data=View Steps Data VisualDelaySeconds=Visual Delay Voltage=Voltage Vsync=Wait For Vsync +Warps=Warps WideScreen16_9=Widescreen WideScreen16_10=Widescreen Windowed=Display Mode @@ -1975,6 +1990,7 @@ Done=Done HeaderText= Space=Space "%s" is an invalid floating point value.="%s" is an invalid floating point value. +"%s" is an invalid integer value.="%s" is an invalid integer value. AnswerCaret=_ AnswerBlank= diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index 575c0b4d0c..7e39fe1231 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -4227,6 +4227,9 @@ Fallback="ScreenMiniMenu" [ScreenMiniMenuTimingDataInformation] Fallback="ScreenMiniMenu" +[ScreenMiniMenuTimingDataChangeInformation] +Fallback="ScreenMiniMenu" + [ScreenMiniMenuBackgroundChange] Fallback="ScreenMiniMenu" diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 0b65fe747e..5f3bc3948c 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -94,6 +94,7 @@ AutoScreenMessage( SM_DoRevertToLastSave ); AutoScreenMessage( SM_DoRevertFromDisk ); AutoScreenMessage( SM_ConfirmClearArea ); AutoScreenMessage( SM_BackFromTimingDataInformation ); +AutoScreenMessage(SM_BackFromTimingDataChangeInformation); AutoScreenMessage( SM_BackFromDifficultyMeterChange ); AutoScreenMessage( SM_BackFromBeat0Change ); AutoScreenMessage( SM_BackFromBPMChange ); @@ -1033,6 +1034,18 @@ static MenuDef g_TimingDataInformation( MenuRowDef(ScreenEdit::fake, "Edit fake", true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::shift_timing_in_region_down, + "Shift timing in region down", + true, EditMode_Full, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd"), + MenuRowDef(ScreenEdit::shift_timing_in_region_up, + "Shift timing in region up", + true, EditMode_Full, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd"), + MenuRowDef(ScreenEdit::copy_timing_in_region, + "Copy 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), MenuRowDef(ScreenEdit::copy_full_timing, "Copy timing data", true, EditMode_Full, true, true, 0, NULL ), @@ -1044,6 +1057,47 @@ static MenuDef g_TimingDataInformation( true, EditMode_Full, true, true, 0, NULL ) ); +static MenuDef g_TimingDataChangeInformation( + "ScreenMiniMenuTimingDataChangeInformation", + MenuRowDef(ScreenEdit::timing_all, + "All timing", + true, EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::timing_bpm, + "BPM changes", + true, EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::timing_stop, + "Stops", + true, EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::timing_delay, + "Delays", + true, EditMode_Full, true, true, 0, NULL), + // Time signatures disabled because they don't fully work. -Kyz + // MenuRowDef(ScreenEdit::timing_time_sig, + // "Time Signatures", + // true, EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::timing_warp, + "Warps", + true, EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::timing_label, + "Labels", + true, EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::timing_tickcount, + "Tickcount", + true, EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::timing_combo, + "Combo segments", + true, EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::timing_speed, + "Speed segments", + true, EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::timing_scroll, + "Scroll segments", + true, EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::timing_fake, + "Fakes", + true, EditMode_Full, true, true, 0, NULL) +); + // XXX: What are these enums used for? enum { @@ -3541,6 +3595,10 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) { HandleTimingDataInformationChoice( (TimingDataInformationChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); } + else if(SM == SM_BackFromTimingDataChangeInformation) + { + HandleTimingDataChangeChoice((TimingDataChangeChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers); + } else if( SM == SM_BackFromDifficultyMeterChange ) { int i = StringToInt( ScreenTextEntry::s_sLastAnswer ); @@ -4628,6 +4686,12 @@ void ScreenEdit::DisplayTimingMenu() EditMiniMenu( &g_TimingDataInformation, SM_BackFromTimingDataInformation ); } +void ScreenEdit::DisplayTimingChangeMenu() +{ + int row= GetRow(); + EditMiniMenu(&g_TimingDataChangeInformation, SM_BackFromTimingDataChangeInformation); +} + // End helper functions static LocalizedString REVERT_LAST_SAVE ( "ScreenEdit", "Do you want to revert to your last save?" ); @@ -5303,6 +5367,13 @@ void ScreenEdit::HandleAlterMenuChoice(AlterMenuChoice c, const vector &iAn } +int GetRowsFromAnswers(int choice, const vector& answers); +int GetRowsFromAnswers(int choice, const vector& answers) +{ + // Logic courtesy of someone else, not me. Just deduplicating it. -Kyz + return answers.size() > 0 ? NoteTypeToRow((NoteType)answers[choice]) : 48; +} + void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAnswers, bool bAllowUndo ) { bool bSaveUndo = true; @@ -5355,23 +5426,19 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAns case insert_and_shift: NoteDataUtil::InsertRows( m_NoteDataEdit, GetRow(), - iAnswers.size() > 0 ? - NoteTypeToRow((NoteType)iAnswers[c]) : 48); + GetRowsFromAnswers(c, iAnswers)); break; case delete_and_shift: NoteDataUtil::DeleteRows( m_NoteDataEdit, GetRow(), - iAnswers.size() > 0 ? - NoteTypeToRow((NoteType)iAnswers[c]) : 48); + GetRowsFromAnswers(c, iAnswers)); break; case shift_pauses_forward: GetAppropriateTimingForUpdate().InsertRows( GetRow(), - iAnswers.size() > 0 ? - NoteTypeToRow((NoteType)iAnswers[c]) : 48); + GetRowsFromAnswers(c, iAnswers)); break; case shift_pauses_backward: GetAppropriateTimingForUpdate().DeleteRows( GetRow(), - iAnswers.size() > 0 ? - NoteTypeToRow((NoteType)iAnswers[c]) : 48); + GetRowsFromAnswers(c, iAnswers)); break; case convert_pause_to_beat: @@ -5501,7 +5568,7 @@ void ScreenEdit::HandleStepsInformationChoice( StepsInformationChoice c, const v ENTER_NEW_METER, ssprintf("%d", m_pSteps->GetMeter()), 4, - NULL, + ScreenTextEntry::IntValidate, ChangeStepMeter, NULL); break; @@ -5747,6 +5814,23 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice ); break; } + case shift_timing_in_region_down: + m_timing_is_being_copied= false; + m_timing_rows_being_shitted= GetRowsFromAnswers(c, iAnswers); + DisplayTimingChangeMenu(); + break; + case shift_timing_in_region_up: + m_timing_is_being_copied= false; + m_timing_rows_being_shitted= -GetRowsFromAnswers(c, iAnswers); + DisplayTimingChangeMenu(); + break; + case copy_timing_in_region: + m_timing_is_being_copied= true; + DisplayTimingChangeMenu(); + break; + case paste_timing_from_clip: + clipboardFullTiming.CopyRange(0, MAX_NOTE_ROW, TimingSegmentType_Invalid, GetRow(), GetAppropriateTimingForUpdate()); + break; case copy_full_timing: { clipboardFullTiming = GetAppropriateTiming(); @@ -5772,6 +5856,71 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice } } +void ScreenEdit::HandleTimingDataChangeChoice(TimingDataChangeChoice choice, + const vector& answers) +{ + TimingSegmentType change_type= TimingSegmentType_Invalid; + switch(choice) + { + case timing_all: + change_type= TimingSegmentType_Invalid; + break; + case timing_bpm: + change_type= SEGMENT_BPM; + break; + case timing_stop: + change_type= SEGMENT_STOP; + break; + case timing_delay: + change_type= SEGMENT_DELAY; + break; + case timing_time_sig: + change_type= SEGMENT_TIME_SIG; + break; + case timing_warp: + change_type= SEGMENT_WARP; + break; + case timing_label: + change_type= SEGMENT_LABEL; + break; + case timing_tickcount: + change_type= SEGMENT_TICKCOUNT; + break; + case timing_combo: + change_type= SEGMENT_COMBO; + break; + case timing_speed: + change_type= SEGMENT_SPEED; + break; + case timing_scroll: + change_type= SEGMENT_SCROLL; + break; + case timing_fake: + change_type= SEGMENT_FAKE; + break; + default: break; + } + int begin= m_NoteFieldEdit.m_iBeginMarker; + int end= m_NoteFieldEdit.m_iEndMarker; + if(begin < 0) + { + begin= GetRow(); + } + if(end < 0) + { + end= MAX_NOTE_ROW; + } + if(m_timing_is_being_copied) + { + clipboardFullTiming.Clear(); + GetAppropriateTiming().CopyRange(begin, end, change_type, 0, clipboardFullTiming); + } + else + { + GetAppropriateTimingForUpdate().ShiftRange(begin, end, change_type, m_timing_rows_being_shitted); + } +} + void ScreenEdit::HandleBGChangeChoice( BGChangeChoice c, const vector &iAnswers ) { BackgroundChange newChange; diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h index 1946adfdca..590014a0b1 100644 --- a/src/ScreenEdit.h +++ b/src/ScreenEdit.h @@ -268,6 +268,10 @@ protected: /** @brief Display the TimingData menu for editing song and step timing. */ void DisplayTimingMenu(); + bool m_timing_is_being_copied; // Instead of being shifted. + int m_timing_rows_being_shitted; // How far the timing is being shitted. + void DisplayTimingChangeMenu(); + EditState m_EditState; Song* m_pSong; @@ -603,15 +607,37 @@ public: speed_mode, scroll, fake, + shift_timing_in_region_down, + shift_timing_in_region_up, + copy_timing_in_region, + paste_timing_from_clip, copy_full_timing, paste_full_timing, erase_step_timing, NUM_TIMING_DATA_INFORMATION_CHOICES }; - - void HandleTimingDataInformationChoice (TimingDataInformationChoice c, + void HandleTimingDataInformationChoice (TimingDataInformationChoice c, const vector &iAnswers ); + enum TimingDataChangeChoice + { + timing_all, + timing_bpm, + timing_stop, + timing_delay, + timing_time_sig, + timing_warp, + timing_label, + timing_tickcount, + timing_combo, + timing_speed, + timing_scroll, + timing_fake, + NUM_TimingDataChangeChoices + }; + void HandleTimingDataChangeChoice(TimingDataChangeChoice choice, + const vector& answers); + enum BGChangeChoice { layer, diff --git a/src/ScreenTextEntry.cpp b/src/ScreenTextEntry.cpp index f8d37b687b..f053c940e0 100644 --- a/src/ScreenTextEntry.cpp +++ b/src/ScreenTextEntry.cpp @@ -108,6 +108,16 @@ bool ScreenTextEntry::FloatValidate( const RString &sAnswer, RString &sErrorOut return false; } +static LocalizedString INVALID_INT( "ScreenTextEntry", "\"%s\" is an invalid integer value." ); +bool ScreenTextEntry::IntValidate( const RString &sAnswer, RString &sErrorOut ) +{ + int f; + if(sAnswer >> f) + return true; + sErrorOut = ssprintf( INVALID_INT.GetValue(), sAnswer.c_str() ); + return false; +} + bool ScreenTextEntry::s_bCancelledLast = false; /* Handle UTF-8. Right now, we need to at least be able to backspace a whole diff --git a/src/ScreenTextEntry.h b/src/ScreenTextEntry.h index ee78688dff..7c15eef8b4 100644 --- a/src/ScreenTextEntry.h +++ b/src/ScreenTextEntry.h @@ -97,6 +97,7 @@ public: void LoadFromTextEntrySettings( const TextEntrySettings &settings ); static bool FloatValidate( const RString &sAnswer, RString &sErrorOut ); + static bool IntValidate( const RString &sAnswer, RString &sErrorOut ); virtual void Init(); virtual void BeginScreen(); diff --git a/src/TimingData.cpp b/src/TimingData.cpp index c684de6ff6..c5e2f02cf2 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -8,6 +8,9 @@ #include "Foreach.h" #include +static void EraseSegment(vector &vSegs, int index, TimingSegment *cur); +static const int INVALID_INDEX = -1; + TimingSegment* GetSegmentAtRow( int iNoteRow, TimingSegmentType tst ); TimingData::TimingData(float fOffset) : m_fBeat0OffsetInSeconds(fOffset) @@ -189,6 +192,72 @@ void TimingData::CopyRange(int start_row, int end_row, } } +void TimingData::ShiftRange(int start_row, int end_row, + TimingSegmentType shift_type, int shift_amount) +{ + FOREACH_TimingSegmentType(seg_type) + { + if(seg_type == shift_type || shift_type == TimingSegmentType_Invalid) + { + vector& segs= GetTimingSegments(seg_type); + int first_row= min(start_row, start_row + shift_amount); + int last_row= max(end_row, end_row + shift_amount); + int first_affected= GetSegmentIndexAtRow(seg_type, first_row); + int last_affected= GetSegmentIndexAtRow(seg_type, last_row); + if(first_affected == INVALID_INDEX) + { + continue; + } + // Prance through the affected area twice. The first time, changing + // the rows of the segments, the second time removing segments that + // have been run over by a segment being moved. Attempts to combine + // both operations into a single loop were error prone. -Kyz + for(size_t i= first_affected; i <= last_affected && i < segs.size(); ++i) + { + int seg_row= segs[i]->GetRow(); + if(seg_row > 0 && seg_row >= start_row && seg_row <= end_row) + { + int dest_row= max(seg_row + shift_amount, 0); + segs[i]->SetRow(dest_row); + } + } +#define ERASE_SEG(s) if(segs.size() > 1) { EraseSegment(segs, s, segs[s]); --i; --last_affected; erased= true; } + for(size_t i= first_affected; i <= last_affected && i < segs.size(); ++i) + { + bool erased= false; + int seg_row= segs[i]->GetRow(); + if(i < segs.size() - 1) + { + int next_row= segs[i+1]->GetRow(); + // This is a loop so that it will go back through and remove all + // segments that were run over. -Kyz + while(seg_row >= next_row && seg_row < start_row) + { + ERASE_SEG(i); + if(i < segs.size()) + { + seg_row= segs[i]->GetRow(); + } + else + { + seg_row= -1; + } + } + } + if(!erased && i > 0) + { + int prev_row= segs[i-1]->GetRow(); + if(prev_row >= seg_row) + { + ERASE_SEG(i); + } + } + } +#undef ERASE_SEG + } + } +} + void TimingData::GetActualBPM( float &fMinBPMOut, float &fMaxBPMOut, float highest ) const { fMinBPMOut = FLT_MAX; @@ -232,8 +301,6 @@ float TimingData::GetPreviousSegmentBeatAtRow(TimingSegmentType tst, int row) co return (backup > -1) ? backup : NoteRowToBeat(row); } -static const int INVALID_INDEX = -1; - int TimingData::GetSegmentIndexAtRow(TimingSegmentType tst, int iRow ) const { const vector &vSegs = GetTimingSegments(tst); diff --git a/src/TimingData.h b/src/TimingData.h index fcad1f125b..3ef0b332f3 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -150,6 +150,7 @@ public: bool empty() const; 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); /** * @brief Gets the actual BPM of the song, * while respecting a limit.