diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/mptrack/PatternFindReplace.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/PatternFindReplace.cpp')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/mptrack/PatternFindReplace.cpp | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/PatternFindReplace.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/PatternFindReplace.cpp new file mode 100644 index 00000000..a87b9a53 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/mptrack/PatternFindReplace.cpp @@ -0,0 +1,597 @@ +/* + * PatternFindReplace.cpp + * ---------------------- + * Purpose: Implementation of the pattern search. + * Notes : (currently none) + * Authors: Olivier Lapicque + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "Mainfrm.h" +#include "Moddoc.h" +#include "resource.h" +#include "View_pat.h" +#include "PatternEditorDialogs.h" +#include "PatternFindReplace.h" +#include "PatternFindReplaceDlg.h" +#include "../soundlib/mod_specifications.h" + +OPENMPT_NAMESPACE_BEGIN + +FindReplace FindReplace::instance; + +FindReplace::FindReplace() + : findFlags(FullSearch), replaceFlags(ReplaceAll) + , replaceNoteAction(ReplaceValue), replaceInstrAction(ReplaceValue), replaceVolumeAction(ReplaceValue), replaceParamAction(ReplaceValue) + , replaceNote(NOTE_NONE), replaceInstr(0), replaceVolume(0), replaceParam(0) + , replaceVolCmd(VOLCMD_NONE), replaceCommand(CMD_NONE) + , findNoteMin(NOTE_NONE), findNoteMax(NOTE_NONE) + , findInstrMin(0), findInstrMax(0) + , findVolCmd(VOLCMD_NONE) + , findVolumeMin(0), findVolumeMax(0) + , findCommand(CMD_NONE) + , findParamMin(0), findParamMax(0) + , selection(PatternRect()) + , findChnMin(0), findChnMax(0) +{ } + + +void CViewPattern::OnEditFind() +{ + static bool dialogOpen = false; + CModDoc *pModDoc = GetDocument(); + if (pModDoc && !dialogOpen) + { + CSoundFile &sndFile = pModDoc->GetSoundFile(); + FindReplace settings = FindReplace::instance; + ModCommand m = ModCommand::Empty(); + if(m_Selection.GetUpperLeft() != m_Selection.GetLowerRight()) + { + settings.findFlags.set(FindReplace::InPatSelection); + settings.findFlags.reset(FindReplace::FullSearch); + } else if(sndFile.Patterns.IsValidPat(m_nPattern)) + { + const CPattern &pat = sndFile.Patterns[m_nPattern]; + m_Cursor.Sanitize(pat.GetNumRows(), pat.GetNumChannels()); + m = *pat.GetpModCommand(m_Cursor.GetRow(), m_Cursor.GetChannel()); + } + + CFindReplaceTab pageFind(IDD_EDIT_FIND, false, sndFile, settings, m); + CFindReplaceTab pageReplace(IDD_EDIT_REPLACE, true, sndFile, settings, m); + CPropertySheet dlg(_T("Find/Replace")); + + dlg.AddPage(&pageFind); + dlg.AddPage(&pageReplace); + dialogOpen = true; + if(dlg.DoModal() == IDOK) + { + FindReplace::instance = settings; + FindReplace::instance.selection = m_Selection; + m_bContinueSearch = false; + OnEditFindNext(); + } + dialogOpen = false; + } +} + + +void CViewPattern::OnEditFindNext() +{ + CSoundFile &sndFile = *GetSoundFile(); + const CModSpecifications &specs = sndFile.GetModSpecifications(); + uint32 nFound = 0; + + if(!FindReplace::instance.findFlags[~FindReplace::FullSearch]) + { + PostMessage(WM_COMMAND, ID_EDIT_FIND); + return; + } + BeginWaitCursor(); + + EffectInfo effectInfo(sndFile); + + PATTERNINDEX patStart = m_nPattern; + PATTERNINDEX patEnd = m_nPattern + 1; + + if(FindReplace::instance.findFlags[FindReplace::FullSearch]) + { + patStart = 0; + patEnd = sndFile.Patterns.Size(); + } else if(FindReplace::instance.findFlags[FindReplace::InPatSelection]) + { + patStart = m_nPattern; + patEnd = patStart + 1; + } + + if(m_bContinueSearch) + { + patStart = m_nPattern; + } + + // Do we search for an extended effect? + bool isExtendedEffect = false; + if(FindReplace::instance.findFlags[FindReplace::Command]) + { + UINT fxndx = effectInfo.GetIndexFromEffect(FindReplace::instance.findCommand, static_cast<ModCommand::PARAM>(FindReplace::instance.findParamMin)); + isExtendedEffect = effectInfo.IsExtendedEffect(fxndx); + } + + CHANNELINDEX firstChannel = 0; + CHANNELINDEX lastChannel = sndFile.GetNumChannels() - 1; + + if(FindReplace::instance.findFlags[FindReplace::InChannels]) + { + // Limit search to given channels + firstChannel = std::min(FindReplace::instance.findChnMin, lastChannel); + lastChannel = std::min(FindReplace::instance.findChnMax, lastChannel); + } + + if(FindReplace::instance.findFlags[FindReplace::InPatSelection]) + { + // Limit search to pattern selection + firstChannel = std::min(FindReplace::instance.selection.GetStartChannel(), lastChannel); + lastChannel = std::min(FindReplace::instance.selection.GetEndChannel(), lastChannel); + } + + for(PATTERNINDEX pat = patStart; pat < patEnd; pat++) + { + if(!sndFile.Patterns.IsValidPat(pat)) + { + continue; + } + + ROWINDEX row = 0; + CHANNELINDEX chn = firstChannel; + if(m_bContinueSearch && pat == patStart && pat == m_nPattern) + { + // Continue search from cursor position + row = GetCurrentRow(); + chn = GetCurrentChannel() + 1; + if(chn > lastChannel) + { + row++; + chn = firstChannel; + } else if(chn < firstChannel) + { + chn = firstChannel; + } + } + + bool firstInPat = true; + const ROWINDEX numRows = sndFile.Patterns[pat].GetNumRows(); + std::vector<ModCommand::INSTR> lastInstr(sndFile.GetNumChannels(), 0); + + for(; row < numRows; row++) + { + ModCommand *m = sndFile.Patterns[pat].GetpModCommand(row, chn); + + for(; chn <= lastChannel; chn++, m++) + { + RowMask findWhere; + + if(FindReplace::instance.findFlags[FindReplace::InPatSelection]) + { + // Limit search to pattern selection + if((chn == FindReplace::instance.selection.GetStartChannel() || chn == FindReplace::instance.selection.GetEndChannel()) + && row >= FindReplace::instance.selection.GetStartRow() && row <= FindReplace::instance.selection.GetEndRow()) + { + // For channels that are on the left and right boundaries of the selection, we need to check + // columns are actually selected a bit more thoroughly. + for(int i = PatternCursor::firstColumn; i <= PatternCursor::lastColumn; i++) + { + PatternCursor cursor(row, chn, static_cast<PatternCursor::Columns>(i)); + if(!FindReplace::instance.selection.Contains(cursor)) + { + switch(i) + { + case PatternCursor::noteColumn: findWhere.note = false; break; + case PatternCursor::instrColumn: findWhere.instrument = false; break; + case PatternCursor::volumeColumn: findWhere.volume = false; break; + case PatternCursor::effectColumn: findWhere.command = false; break; + case PatternCursor::paramColumn: findWhere.parameter = false; break; + } + } + } + } else + { + // For channels inside the selection, we have an easier job to solve. + if(!FindReplace::instance.selection.Contains(PatternCursor(row, chn))) + { + findWhere.Clear(); + } + } + } + + if(m->instr > 0) + lastInstr[chn] = m->instr; + + if((FindReplace::instance.findFlags[FindReplace::Note] && (!findWhere.note || m->note < FindReplace::instance.findNoteMin || m->note > FindReplace::instance.findNoteMax)) + || (FindReplace::instance.findFlags[FindReplace::Instr] && (!findWhere.instrument || m->instr < FindReplace::instance.findInstrMin || m->instr > FindReplace::instance.findInstrMax))) + { + continue; + } + + if(!m->IsPcNote()) + { + if((FindReplace::instance.findFlags[FindReplace::VolCmd] && (!findWhere.volume || m->volcmd != FindReplace::instance.findVolCmd)) + || (FindReplace::instance.findFlags[FindReplace::Volume] && (!findWhere.volume || m->volcmd == VOLCMD_NONE || m->vol < FindReplace::instance.findVolumeMin || m->vol > FindReplace::instance.findVolumeMax)) + || (FindReplace::instance.findFlags[FindReplace::Command] && (!findWhere.command || m->command != FindReplace::instance.findCommand)) + || (FindReplace::instance.findFlags[FindReplace::Param] && (!findWhere.parameter || m->command == CMD_NONE || m->param < FindReplace::instance.findParamMin || m->param > FindReplace::instance.findParamMax)) + || FindReplace::instance.findFlags[FindReplace::PCParam] + || FindReplace::instance.findFlags[FindReplace::PCValue]) + { + continue; + } + } else + { + if((FindReplace::instance.findFlags[FindReplace::PCParam] && (!findWhere.volume || m->GetValueVolCol() < FindReplace::instance.findParamMin || m->GetValueVolCol() > FindReplace::instance.findParamMax)) + || (FindReplace::instance.findFlags[FindReplace::PCValue] && (!(findWhere.command || findWhere.parameter) || m->GetValueEffectCol() < FindReplace::instance.findVolumeMin || m->GetValueEffectCol() > FindReplace::instance.findVolumeMax)) + || FindReplace::instance.findFlags[FindReplace::VolCmd] + || FindReplace::instance.findFlags[FindReplace::Volume] + || FindReplace::instance.findFlags[FindReplace::Command] + || FindReplace::instance.findFlags[FindReplace::Param]) + { + continue; + } + } + + if((FindReplace::instance.findFlags & (FindReplace::Command | FindReplace::Param)) == FindReplace::Command && isExtendedEffect) + { + if((m->param & 0xF0) != (FindReplace::instance.findParamMin & 0xF0)) + continue; + } + + // Found! + + // Do we want to jump to the finding in this pattern? + const bool updatePos = !FindReplace::instance.replaceFlags.test_all(FindReplace::ReplaceAll | FindReplace::Replace); + nFound++; + + if(updatePos) + { + if(IsLiveRecord()) + { + // turn off "follow song" + m_Status.reset(psFollowSong); + SendCtrlMessage(CTRLMSG_PAT_FOLLOWSONG, 0); + } + + // Find sequence and order where this pattern is used + const auto numSequences = sndFile.Order.GetNumSequences(); + auto seq = sndFile.Order.GetCurrentSequenceIndex(); + for(SEQUENCEINDEX i = 0; i < numSequences; i++) + { + const bool isCurrentSeq = (i == 0); + ORDERINDEX matchingOrder = sndFile.Order(seq).FindOrder(pat, isCurrentSeq ? GetCurrentOrder() : 0); + if(matchingOrder != ORDERINDEX_INVALID) + { + if(!isCurrentSeq) + SendCtrlMessage(CTRLMSG_PAT_SETSEQUENCE, seq); + SetCurrentOrder(matchingOrder); + break; + } + if(++seq >= numSequences) + seq = 0; + } + // go to place of finding + SetCurrentPattern(pat); + } + + PatternCursor::Columns foundCol = PatternCursor::firstColumn; + if(FindReplace::instance.findFlags[FindReplace::Note]) + foundCol = PatternCursor::noteColumn; + else if(FindReplace::instance.findFlags[FindReplace::Instr]) + foundCol = PatternCursor::instrColumn; + else if(FindReplace::instance.findFlags[FindReplace::VolCmd | FindReplace::Volume | FindReplace::PCParam]) + foundCol = PatternCursor::volumeColumn; + else if(FindReplace::instance.findFlags[FindReplace::Command | FindReplace::PCValue]) + foundCol = PatternCursor::effectColumn; + else if(FindReplace::instance.findFlags[FindReplace::Param]) + foundCol = PatternCursor::paramColumn; + + if(updatePos) + { + // Jump to pattern cell + SetCursorPosition(PatternCursor(row, chn, foundCol)); + } + + if(!FindReplace::instance.replaceFlags[FindReplace::Replace]) goto EndSearch; + + bool replace = true; + + if(!FindReplace::instance.replaceFlags[FindReplace::ReplaceAll]) + { + ConfirmAnswer result = Reporting::Confirm("Replace this occurrence?", "Replace", true); + if(result == cnfCancel) + { + goto EndSearch; // Yuck! + } else + { + replace = (result == cnfYes); + } + } + if(replace) + { + if(FindReplace::instance.replaceFlags[FindReplace::ReplaceAll]) + { + // Just create one logic undo step per pattern when auto-replacing all occurences. + if(firstInPat) + { + GetDocument()->GetPatternUndo().PrepareUndo(pat, firstChannel, row, lastChannel - firstChannel + 1, numRows - row + 1, "Find / Replace", (nFound > 1)); + firstInPat = false; + } + } else + { + // Create separately undo-able items when replacing manually. + GetDocument()->GetPatternUndo().PrepareUndo(pat, chn, row, 1, 1, "Find / Replace"); + } + + if(FindReplace::instance.replaceFlags[FindReplace::Instr]) + { + int instrReplace = FindReplace::instance.replaceInstr; + int instr = m->instr; + if(FindReplace::instance.replaceInstrAction == FindReplace::ReplaceRelative && instr > 0) + instr += instrReplace; + else if(FindReplace::instance.replaceInstrAction == FindReplace::ReplaceValue) + instr = instrReplace; + + m->instr = mpt::saturate_cast<ModCommand::INSTR>(instr); + if(m->instr > 0) + lastInstr[chn] = m->instr; + } + + if(FindReplace::instance.replaceFlags[FindReplace::Note]) + { + int noteReplace = FindReplace::instance.replaceNote; + if(FindReplace::instance.replaceNoteAction == FindReplace::ReplaceRelative && m->IsNote()) + { + if(noteReplace == FindReplace::ReplaceOctaveUp || noteReplace == FindReplace::ReplaceOctaveDown) + { + noteReplace = GetDocument()->GetInstrumentGroupSize(lastInstr[chn]) * mpt::signum(noteReplace); + } + int note = Clamp(m->note + noteReplace, specs.noteMin, specs.noteMax); + m->note = static_cast<ModCommand::NOTE>(note); + } else if(FindReplace::instance.replaceNoteAction == FindReplace::ReplaceValue) + { + // Replace with another note + // If we're going to remove a PC Note or replace a normal note by a PC note, wipe out the complete column. + if(m->IsPcNote() != ModCommand::IsPcNote(static_cast<ModCommand::NOTE>(noteReplace))) + { + m->Clear(); + } + m->note = static_cast<ModCommand::NOTE>(noteReplace); + } + } + + bool hadVolume = (m->volcmd == VOLCMD_VOLUME); + if(FindReplace::instance.replaceFlags[FindReplace::VolCmd]) + { + m->volcmd = FindReplace::instance.replaceVolCmd; + } + + if(FindReplace::instance.replaceFlags[FindReplace::Volume]) + { + int volReplace = FindReplace::instance.replaceVolume; + int vol = m->vol; + if(FindReplace::instance.replaceVolumeAction == FindReplace::ReplaceRelative || FindReplace::instance.replaceVolumeAction == FindReplace::ReplaceMultiply) + { + if(!hadVolume && m->volcmd == VOLCMD_VOLUME) + vol = GetDefaultVolume(*m, lastInstr[chn]); + + if(FindReplace::instance.replaceVolumeAction == FindReplace::ReplaceRelative) + vol += volReplace; + else + vol = Util::muldivr(vol, volReplace, 100); + } else if(FindReplace::instance.replaceVolumeAction == FindReplace::ReplaceValue) + { + vol = volReplace; + } + m->vol = mpt::saturate_cast<ModCommand::VOL>(vol); + } + + if(FindReplace::instance.replaceFlags[FindReplace::VolCmd | FindReplace::Volume] && m->volcmd != VOLCMD_NONE) + { + // Fix volume command parameters if necessary. This is necesary e.g. + // when there was a command "v24" and the user searched for v and replaced it by d. + // In that case, d24 wouldn't be a valid command. + ModCommand::VOL minVal = 0, maxVal = 64; + if(effectInfo.GetVolCmdInfo(effectInfo.GetIndexFromVolCmd(m->volcmd), nullptr, &minVal, &maxVal)) + { + Limit(m->vol, minVal, maxVal); + } + } + + hadVolume = (m->command == CMD_VOLUME); + if(FindReplace::instance.replaceFlags[FindReplace::Command]) + { + m->command = FindReplace::instance.replaceCommand; + } + + if(FindReplace::instance.replaceFlags[FindReplace::Param]) + { + int paramReplace = FindReplace::instance.replaceParam; + int param = m->param; + if(FindReplace::instance.replaceParamAction == FindReplace::ReplaceRelative || FindReplace::instance.replaceParamAction == FindReplace::ReplaceMultiply) + { + if(isExtendedEffect) + param &= 0x0F; + + if(!hadVolume && m->command == CMD_VOLUME) + param = GetDefaultVolume(*m, lastInstr[chn]); + + if(FindReplace::instance.replaceParamAction == FindReplace::ReplaceRelative) + param += paramReplace; + else + param = Util::muldivr(param, paramReplace, 100); + + if(isExtendedEffect) + param = Clamp(param, 0, 15) | (m->param & 0xF0); + } else if(FindReplace::instance.replaceParamAction == FindReplace::ReplaceValue) + { + param = paramReplace; + } + + if(isExtendedEffect && !FindReplace::instance.replaceFlags[FindReplace::Command]) + m->param = static_cast<ModCommand::PARAM>((m->param & 0xF0) | (param & 0x0F)); + else + m->param = mpt::saturate_cast<ModCommand::PARAM>(param); + } + + if(FindReplace::instance.replaceFlags[FindReplace::PCParam]) + { + int paramReplace = FindReplace::instance.replaceParam; + int param = m->GetValueVolCol(); + if(FindReplace::instance.replaceParamAction == FindReplace::ReplaceRelative) + param += paramReplace; + else if(FindReplace::instance.replaceParamAction == FindReplace::ReplaceMultiply) + param = Util::muldivr(param, paramReplace, 100); + else if(FindReplace::instance.replaceParamAction == FindReplace::ReplaceValue) + param = paramReplace; + + m->SetValueVolCol(static_cast<uint16>(Clamp(param, 0, ModCommand::maxColumnValue))); + } + + if(FindReplace::instance.replaceFlags[FindReplace::PCValue]) + { + int valueReplace = FindReplace::instance.replaceVolume; + int value = m->GetValueEffectCol(); + if(FindReplace::instance.replaceVolumeAction == FindReplace::ReplaceRelative) + value += valueReplace; + else if(FindReplace::instance.replaceVolumeAction == FindReplace::ReplaceMultiply) + value = Util::muldivr(value, valueReplace, 100); + else if(FindReplace::instance.replaceVolumeAction == FindReplace::ReplaceValue) + value = valueReplace; + + m->SetValueEffectCol(static_cast<uint16>(Clamp(value, 0, ModCommand::maxColumnValue))); + } + + SetModified(false); + if(updatePos) + InvalidateRow(); + } + } + chn = firstChannel; + } + + } +EndSearch: + + if(FindReplace::instance.replaceFlags[FindReplace::ReplaceAll]) + { + InvalidatePattern(); + } + + if(FindReplace::instance.findFlags[FindReplace::InPatSelection] && (nFound == 0 || (FindReplace::instance.replaceFlags & (FindReplace::Replace | FindReplace::ReplaceAll)) == FindReplace::Replace)) + { + // Restore original selection if we didn't find anything or just replaced stuff manually. + m_Selection = FindReplace::instance.selection; + InvalidatePattern(); + } + + m_bContinueSearch = true; + + EndWaitCursor(); + + // Display search results + if(nFound == 0) + { + CString result; + result.Preallocate(14 + 16); + result = _T("Cannot find \""); + + // Note + if(FindReplace::instance.findFlags[FindReplace::Note]) + { + result += mpt::ToCString(sndFile.GetNoteName(FindReplace::instance.findNoteMin)); + if(FindReplace::instance.findNoteMax > FindReplace::instance.findNoteMin) + { + result.AppendChar(_T('-')); + result += mpt::ToCString(sndFile.GetNoteName(FindReplace::instance.findNoteMax)); + } + } else + { + result += _T("???"); + } + result.AppendChar(_T(' ')); + + // Instrument + if(FindReplace::instance.findFlags[FindReplace::Instr]) + { + if(FindReplace::instance.findInstrMin) + result.AppendFormat(_T("%03d"), FindReplace::instance.findInstrMin); + else + result.Append(_T(" ..")); + if(FindReplace::instance.findInstrMax > FindReplace::instance.findInstrMin) + result.AppendFormat(_T("-%03d"), FindReplace::instance.findInstrMax); + } else + { + result.Append(_T(" ??")); + } + result.AppendChar(_T(' ')); + + // Volume Command + if(FindReplace::instance.findFlags[FindReplace::VolCmd]) + { + if(FindReplace::instance.findVolCmd != VOLCMD_NONE) + result.AppendChar(specs.GetVolEffectLetter(FindReplace::instance.findVolCmd)); + else + result.AppendChar(_T('.')); + } else if(FindReplace::instance.findFlags[FindReplace::PCParam]) + { + result.AppendFormat(_T("%03d"), FindReplace::instance.findParamMin); + if(FindReplace::instance.findParamMax > FindReplace::instance.findParamMin) + result.AppendFormat(_T("-%03d"), FindReplace::instance.findParamMax); + } else + { + result.AppendChar(_T('?')); + } + + // Volume Parameter + if(FindReplace::instance.findFlags[FindReplace::Volume]) + { + result.AppendFormat(_T("%02d"), FindReplace::instance.findVolumeMin); + if(FindReplace::instance.findVolumeMax > FindReplace::instance.findVolumeMin) + result.AppendFormat(_T("-%02d"), FindReplace::instance.findVolumeMax); + } else if(!FindReplace::instance.findFlags[FindReplace::PCParam]) + { + result.AppendFormat(_T("??")); + } + result.AppendChar(_T(' ')); + + // Effect Command + if(FindReplace::instance.findFlags[FindReplace::Command]) + { + if(FindReplace::instance.findCommand != CMD_NONE) + result.AppendChar(specs.GetEffectLetter(FindReplace::instance.findCommand)); + else + result.AppendChar(_T('.')); + } else if(FindReplace::instance.findFlags[FindReplace::PCValue]) + { + result.AppendFormat(_T("%03d"), FindReplace::instance.findVolumeMin); + if(FindReplace::instance.findVolumeMax > FindReplace::instance.findVolumeMin) + result.AppendFormat(_T("-%03d"), FindReplace::instance.findVolumeMax); + } else + { + result.AppendChar(_T('?')); + } + + // Effect Parameter + if(FindReplace::instance.findFlags[FindReplace::Param]) + { + result.AppendFormat(_T("%02X"), FindReplace::instance.findParamMin); + if(FindReplace::instance.findParamMax > FindReplace::instance.findParamMin) + result.AppendFormat(_T("-%02X"), FindReplace::instance.findParamMax); + } else if(!FindReplace::instance.findFlags[FindReplace::PCValue]) + { + result.AppendFormat(_T("??")); + } + + result.AppendChar(_T('"')); + + Reporting::Information(result, _T("Find/Replace")); + } +} + +OPENMPT_NAMESPACE_END |