1303 lines
39 KiB
C++
1303 lines
39 KiB
C++
/*
|
|
* Ctrl_pat.cpp
|
|
* ------------
|
|
* Purpose: Pattern tab, upper panel.
|
|
* 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 "Mptrack.h"
|
|
#include "Mainfrm.h"
|
|
#include "InputHandler.h"
|
|
#include "ImageLists.h"
|
|
#include "Childfrm.h"
|
|
#include "Moddoc.h"
|
|
#include "../soundlib/mod_specifications.h"
|
|
#include "Globals.h"
|
|
#include "Ctrl_pat.h"
|
|
#include "View_pat.h"
|
|
#include "PatternEditorDialogs.h"
|
|
#include "ChannelManagerDlg.h"
|
|
#include "../common/mptStringBuffer.h"
|
|
|
|
|
|
OPENMPT_NAMESPACE_BEGIN
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// CCtrlPatterns
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CCtrlPatterns, CModControlDlg)
|
|
//{{AFX_MSG_MAP(CCtrlPatterns)
|
|
ON_WM_KEYDOWN()
|
|
ON_WM_VSCROLL()
|
|
ON_WM_XBUTTONUP()
|
|
ON_COMMAND(IDC_BUTTON1, &CCtrlPatterns::OnSequenceNext)
|
|
ON_COMMAND(IDC_BUTTON2, &CCtrlPatterns::OnSequencePrev)
|
|
ON_COMMAND(ID_PLAYER_PAUSE, &CCtrlPatterns::OnPlayerPause)
|
|
ON_COMMAND(IDC_PATTERN_NEW, &CCtrlPatterns::OnPatternNew)
|
|
ON_COMMAND(IDC_PATTERN_STOP, &CCtrlPatterns::OnPatternStop)
|
|
ON_COMMAND(IDC_PATTERN_PLAY, &CCtrlPatterns::OnPatternPlay)
|
|
ON_COMMAND(IDC_PATTERN_PLAYFROMSTART, &CCtrlPatterns::OnPatternPlayFromStart)
|
|
ON_COMMAND(IDC_PATTERN_RECORD, &CCtrlPatterns::OnPatternRecord)
|
|
ON_COMMAND(IDC_PATTERN_LOOP, &CCtrlPatterns::OnChangeLoopStatus)
|
|
ON_COMMAND(ID_PATTERN_PLAYROW, &CCtrlPatterns::OnPatternPlayRow)
|
|
ON_COMMAND(ID_PATTERN_CHANNELMANAGER, &CCtrlPatterns::OnChannelManager)
|
|
ON_COMMAND(ID_PATTERN_VUMETERS, &CCtrlPatterns::OnPatternVUMeters)
|
|
ON_COMMAND(ID_VIEWPLUGNAMES, &CCtrlPatterns::OnPatternViewPlugNames)
|
|
ON_COMMAND(ID_NEXTINSTRUMENT, &CCtrlPatterns::OnNextInstrument)
|
|
ON_COMMAND(ID_PREVINSTRUMENT, &CCtrlPatterns::OnPrevInstrument)
|
|
ON_COMMAND(IDC_PATTERN_FOLLOWSONG, &CCtrlPatterns::OnFollowSong)
|
|
ON_COMMAND(ID_PATTERN_CHORDEDIT, &CCtrlPatterns::OnChordEditor)
|
|
ON_COMMAND(ID_PATTERN_PROPERTIES, &CCtrlPatterns::OnPatternProperties)
|
|
ON_COMMAND(ID_PATTERN_EXPAND, &CCtrlPatterns::OnPatternExpand)
|
|
ON_COMMAND(ID_PATTERN_SHRINK, &CCtrlPatterns::OnPatternShrink)
|
|
ON_COMMAND(ID_PATTERN_AMPLIFY, &CCtrlPatterns::OnPatternAmplify)
|
|
ON_COMMAND(ID_ORDERLIST_NEW, &CCtrlPatterns::OnPatternNew)
|
|
ON_COMMAND(ID_ORDERLIST_COPY, &CCtrlPatterns::OnPatternDuplicate)
|
|
ON_COMMAND(ID_ORDERLIST_MERGE, &CCtrlPatterns::OnPatternMerge)
|
|
ON_COMMAND(ID_PATTERNCOPY, &CCtrlPatterns::OnPatternCopy)
|
|
ON_COMMAND(ID_PATTERNPASTE, &CCtrlPatterns::OnPatternPaste)
|
|
ON_COMMAND(ID_EDIT_UNDO, &CCtrlPatterns::OnEditUndo)
|
|
ON_COMMAND(ID_PATTERNDETAIL_LO, &CCtrlPatterns::OnDetailLo)
|
|
ON_COMMAND(ID_PATTERNDETAIL_MED, &CCtrlPatterns::OnDetailMed)
|
|
ON_COMMAND(ID_PATTERNDETAIL_HI, &CCtrlPatterns::OnDetailHi)
|
|
ON_COMMAND(ID_OVERFLOWPASTE, &CCtrlPatterns::OnToggleOverflowPaste)
|
|
ON_CBN_SELCHANGE(IDC_COMBO_INSTRUMENT, &CCtrlPatterns::OnInstrumentChanged)
|
|
ON_COMMAND(IDC_PATINSTROPLUGGUI, &CCtrlPatterns::TogglePluginEditor) //rewbs.instroVST
|
|
ON_EN_CHANGE(IDC_EDIT_SPACING, &CCtrlPatterns::OnSpacingChanged)
|
|
ON_EN_CHANGE(IDC_EDIT_PATTERNNAME, &CCtrlPatterns::OnPatternNameChanged)
|
|
ON_EN_CHANGE(IDC_EDIT_SEQUENCE_NAME, &CCtrlPatterns::OnSequenceNameChanged)
|
|
ON_EN_CHANGE(IDC_EDIT_SEQNUM, &CCtrlPatterns::OnSequenceNumChanged)
|
|
ON_UPDATE_COMMAND_UI(IDC_PATTERN_RECORD,&CCtrlPatterns::OnUpdateRecord)
|
|
ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &CCtrlPatterns::OnToolTipText)
|
|
//}}AFX_MSG_MAP
|
|
ON_WM_MOUSEWHEEL()
|
|
END_MESSAGE_MAP()
|
|
|
|
void CCtrlPatterns::DoDataExchange(CDataExchange *pDX)
|
|
{
|
|
CModControlDlg::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(CCtrlPatterns)
|
|
DDX_Control(pDX, IDC_BUTTON1, m_BtnNext);
|
|
DDX_Control(pDX, IDC_BUTTON2, m_BtnPrev);
|
|
DDX_Control(pDX, IDC_COMBO_INSTRUMENT, m_CbnInstrument);
|
|
DDX_Control(pDX, IDC_EDIT_SPACING, m_EditSpacing);
|
|
DDX_Control(pDX, IDC_EDIT_PATTERNNAME, m_EditPatName);
|
|
DDX_Control(pDX, IDC_EDIT_SEQNUM, m_EditSequence);
|
|
DDX_Control(pDX, IDC_SPIN_SPACING, m_SpinSpacing);
|
|
DDX_Control(pDX, IDC_SPIN_INSTRUMENT, m_SpinInstrument);
|
|
DDX_Control(pDX, IDC_SPIN_SEQNUM, m_SpinSequence);
|
|
DDX_Control(pDX, IDC_TOOLBAR1, m_ToolBar);
|
|
//}}AFX_DATA_MAP
|
|
}
|
|
|
|
|
|
const ModSequence &CCtrlPatterns::Order() const { return m_sndFile.Order(); }
|
|
ModSequence &CCtrlPatterns::Order() { return m_sndFile.Order(); }
|
|
|
|
|
|
CCtrlPatterns::CCtrlPatterns(CModControlView &parent, CModDoc &document)
|
|
: CModControlDlg(parent, document), m_OrderList(*this, document)
|
|
{
|
|
m_bVUMeters = TrackerSettings::Instance().gbPatternVUMeters;
|
|
m_bPluginNames = TrackerSettings::Instance().gbPatternPluginNames;
|
|
m_bRecord = TrackerSettings::Instance().gbPatternRecord;
|
|
}
|
|
|
|
|
|
BOOL CCtrlPatterns::OnInitDialog()
|
|
{
|
|
CRect rect, rcOrderList;
|
|
CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
|
|
CModControlDlg::OnInitDialog();
|
|
EnableToolTips();
|
|
|
|
if(!pMainFrm)
|
|
return TRUE;
|
|
SetRedraw(FALSE);
|
|
LockControls();
|
|
// Order List
|
|
m_BtnNext.GetWindowRect(&rect);
|
|
ScreenToClient(&rect);
|
|
auto margins = Util::ScalePixels(4, m_hWnd);
|
|
rcOrderList.left = rect.right + margins;
|
|
rcOrderList.top = rect.top;
|
|
rcOrderList.bottom = rect.bottom + GetSystemMetrics(SM_CYHSCROLL);
|
|
GetClientRect(&rect);
|
|
rcOrderList.right = rect.right - margins;
|
|
m_OrderList.Init(rcOrderList, pMainFrm->GetGUIFont());
|
|
// Toolbar buttons
|
|
m_ToolBar.Init(CMainFrame::GetMainFrame()->m_PatternIcons, CMainFrame::GetMainFrame()->m_PatternIconsDisabled);
|
|
m_ToolBar.AddButton(IDC_PATTERN_NEW, TIMAGE_PATTERN_NEW);
|
|
m_ToolBar.AddButton(IDC_PATTERN_PLAY, TIMAGE_PATTERN_PLAY);
|
|
m_ToolBar.AddButton(IDC_PATTERN_PLAYFROMSTART, TIMAGE_PATTERN_RESTART);
|
|
m_ToolBar.AddButton(IDC_PATTERN_STOP, TIMAGE_PATTERN_STOP);
|
|
m_ToolBar.AddButton(ID_PATTERN_PLAYROW, TIMAGE_PATTERN_PLAYROW);
|
|
m_ToolBar.AddButton(IDC_PATTERN_RECORD, TIMAGE_PATTERN_RECORD, TBSTYLE_CHECK, (m_bRecord ? TBSTATE_CHECKED : 0) | TBSTATE_ENABLED);
|
|
m_ToolBar.AddButton(ID_SEPARATOR, 0, TBSTYLE_SEP);
|
|
m_ToolBar.AddButton(ID_PATTERN_VUMETERS, TIMAGE_PATTERN_VUMETERS, TBSTYLE_CHECK, (m_bVUMeters ? TBSTATE_CHECKED : 0) | TBSTATE_ENABLED);
|
|
m_ToolBar.AddButton(ID_VIEWPLUGNAMES, TIMAGE_PATTERN_PLUGINS, TBSTYLE_CHECK, (m_bPluginNames ? TBSTATE_CHECKED : 0) | TBSTATE_ENABLED);
|
|
m_ToolBar.AddButton(ID_PATTERN_CHANNELMANAGER, TIMAGE_CHANNELMANAGER);
|
|
m_ToolBar.AddButton(ID_SEPARATOR, 0, TBSTYLE_SEP);
|
|
m_ToolBar.AddButton(ID_PATTERN_MIDIMACRO, TIMAGE_MACROEDITOR);
|
|
m_ToolBar.AddButton(ID_PATTERN_CHORDEDIT, TIMAGE_CHORDEDITOR);
|
|
m_ToolBar.AddButton(ID_SEPARATOR, 0, TBSTYLE_SEP);
|
|
m_ToolBar.AddButton(ID_EDIT_UNDO, TIMAGE_UNDO);
|
|
m_ToolBar.AddButton(ID_PATTERN_PROPERTIES, TIMAGE_PATTERN_PROPERTIES);
|
|
m_ToolBar.AddButton(ID_PATTERN_EXPAND, TIMAGE_PATTERN_EXPAND);
|
|
m_ToolBar.AddButton(ID_PATTERN_SHRINK, TIMAGE_PATTERN_SHRINK);
|
|
// m_ToolBar.AddButton(ID_PATTERN_AMPLIFY, TIMAGE_SAMPLE_AMPLIFY);
|
|
m_ToolBar.AddButton(ID_SEPARATOR, 0, TBSTYLE_SEP);
|
|
m_ToolBar.AddButton(ID_PATTERNDETAIL_LO, TIMAGE_PATTERN_DETAIL_LO, TBSTYLE_CHECK, TBSTATE_ENABLED);
|
|
m_ToolBar.AddButton(ID_PATTERNDETAIL_MED, TIMAGE_PATTERN_DETAIL_MED, TBSTYLE_CHECK, TBSTATE_ENABLED);
|
|
m_ToolBar.AddButton(ID_PATTERNDETAIL_HI, TIMAGE_PATTERN_DETAIL_HI, TBSTYLE_CHECK, TBSTATE_ENABLED | TBSTATE_CHECKED);
|
|
m_ToolBar.AddButton(ID_SEPARATOR, 0, TBSTYLE_SEP);
|
|
m_ToolBar.AddButton(ID_OVERFLOWPASTE, TIMAGE_PATTERN_OVERFLOWPASTE, TBSTYLE_CHECK, ((TrackerSettings::Instance().m_dwPatternSetup & PATTERN_OVERFLOWPASTE) ? TBSTATE_CHECKED : 0) | TBSTATE_ENABLED);
|
|
|
|
// Special edit controls -> tab switch to view
|
|
m_EditSequence.SetParent(this);
|
|
m_EditSpacing.SetParent(this);
|
|
m_EditPatName.SetParent(this);
|
|
m_EditPatName.SetLimitText(MAX_PATTERNNAME - 1);
|
|
// Spin controls
|
|
m_SpinSpacing.SetRange32(0, MAX_SPACING);
|
|
m_SpinSpacing.SetPos(TrackerSettings::Instance().gnPatternSpacing);
|
|
|
|
m_SpinInstrument.SetRange32(-1, 1);
|
|
m_SpinInstrument.SetPos(0);
|
|
|
|
SetDlgItemInt(IDC_EDIT_SPACING, TrackerSettings::Instance().gnPatternSpacing);
|
|
CheckDlgButton(IDC_PATTERN_FOLLOWSONG, !(TrackerSettings::Instance().m_dwPatternSetup & PATTERN_FOLLOWSONGOFF));
|
|
|
|
m_SpinSequence.SetRange32(1, m_sndFile.Order.GetNumSequences());
|
|
m_SpinSequence.SetPos(m_sndFile.Order.GetCurrentSequenceIndex() + 1);
|
|
SetDlgItemText(IDC_EDIT_SEQUENCE_NAME, mpt::ToCString(Order().GetName()));
|
|
|
|
m_OrderList.SetFocus();
|
|
|
|
UpdateView(PatternHint().Names().ModType(), NULL);
|
|
RecalcLayout();
|
|
|
|
m_bInitialized = TRUE;
|
|
UnlockControls();
|
|
|
|
SetRedraw(TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::RecalcLayout()
|
|
{
|
|
// Update Order List Position
|
|
if(m_OrderList.m_hWnd)
|
|
{
|
|
CRect rect;
|
|
int cx, cy, cellcx;
|
|
|
|
m_BtnNext.GetWindowRect(&rect);
|
|
ScreenToClient(&rect);
|
|
cx = -(rect.right + 4);
|
|
cy = rect.bottom - rect.top + GetSystemMetrics(SM_CYHSCROLL);
|
|
GetClientRect(&rect);
|
|
cx += rect.right - 8;
|
|
cellcx = m_OrderList.GetFontWidth();
|
|
if(cellcx > 0)
|
|
cx -= (cx % cellcx);
|
|
cx += 2;
|
|
if((cx > 0) && (cy > 0))
|
|
{
|
|
m_OrderList.SetWindowPos(NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER | SWP_DRAWFRAME);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::UpdateView(UpdateHint hint, CObject *pObj)
|
|
{
|
|
m_OrderList.UpdateView(hint, pObj);
|
|
FlagSet<HintType> hintType = hint.GetType();
|
|
|
|
const bool updateAll = hintType[HINT_MODTYPE];
|
|
const bool updateSeq = hint.GetCategory() == HINTCAT_SEQUENCE;
|
|
const bool updatePlug = hint.GetCategory() == HINTCAT_PLUGINS && hintType[HINT_MIXPLUGINS];
|
|
const PatternHint patternHint = hint.ToType<PatternHint>();
|
|
|
|
if(updateAll || (updateSeq && hintType[HINT_SEQNAMES]))
|
|
{
|
|
SetDlgItemText(IDC_EDIT_SEQUENCE_NAME, mpt::ToCString(Order().GetName()));
|
|
}
|
|
|
|
if(updateAll || (updateSeq && hintType[HINT_MODSEQUENCE]))
|
|
{
|
|
m_SpinSequence.SetRange(1, m_sndFile.Order.GetNumSequences());
|
|
m_SpinSequence.SetPos(m_sndFile.Order.GetCurrentSequenceIndex() + 1);
|
|
|
|
// Enable/disable multisequence controls according the current modtype.
|
|
const BOOL isMultiSeqAvail = (m_sndFile.GetModSpecifications().sequencesMax > 1 || m_sndFile.Order.GetNumSequences() > 1) ? TRUE : FALSE;
|
|
GetDlgItem(IDC_STATIC_SEQUENCE_NAME)->EnableWindow(isMultiSeqAvail);
|
|
GetDlgItem(IDC_EDIT_SEQUENCE_NAME)->EnableWindow(isMultiSeqAvail);
|
|
GetDlgItem(IDC_EDIT_SEQNUM)->EnableWindow(isMultiSeqAvail);
|
|
GetDlgItem(IDC_SPIN_SEQNUM)->EnableWindow(isMultiSeqAvail);
|
|
}
|
|
|
|
if(updateAll || updatePlug)
|
|
{
|
|
GetDlgItem(IDC_PATINSTROPLUGGUI)->EnableWindow(HasValidPlug(m_nInstrument) ? TRUE : FALSE);
|
|
}
|
|
|
|
if(updateAll)
|
|
{
|
|
// Enable/disable pattern names
|
|
const BOOL isPatNameAvail = m_sndFile.GetModSpecifications().hasPatternNames ? TRUE : FALSE;
|
|
GetDlgItem(IDC_STATIC_PATTERNNAME)->EnableWindow(isPatNameAvail);
|
|
GetDlgItem(IDC_EDIT_PATTERNNAME)->EnableWindow(isPatNameAvail);
|
|
}
|
|
|
|
if(hintType[HINT_MPTOPTIONS])
|
|
{
|
|
m_ToolBar.UpdateStyle();
|
|
m_ToolBar.SetState(ID_OVERFLOWPASTE, ((TrackerSettings::Instance().m_dwPatternSetup & PATTERN_OVERFLOWPASTE) ? TBSTATE_CHECKED : 0) | TBSTATE_ENABLED);
|
|
}
|
|
|
|
bool instrPluginsChanged = false;
|
|
if(hint.GetCategory() == HINTCAT_PLUGINS && hintType[HINT_PLUGINNAMES])
|
|
{
|
|
const auto changedPlug = hint.ToType<PluginHint>().GetPlugin();
|
|
for(INSTRUMENTINDEX i = 1; i <= m_sndFile.GetNumInstruments(); i++)
|
|
{
|
|
const auto ins = m_sndFile.Instruments[i];
|
|
if(ins != nullptr && (!changedPlug && ins->nMixPlug != 0) || (changedPlug && ins->nMixPlug == changedPlug))
|
|
{
|
|
instrPluginsChanged = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool updatePatNames = patternHint.GetType()[HINT_PATNAMES];
|
|
const bool updateSmpNames = hint.GetCategory() == HINTCAT_SAMPLES && hintType[HINT_SMPNAMES];
|
|
const bool updateInsNames = (hint.GetCategory() == HINTCAT_INSTRUMENTS && hintType[HINT_INSNAMES]) || instrPluginsChanged;
|
|
if(updateAll || updatePatNames || updateSmpNames || updateInsNames)
|
|
{
|
|
LockControls();
|
|
CString s;
|
|
if(updateAll || updateSmpNames || updateInsNames)
|
|
{
|
|
constexpr TCHAR szSplitFormat[] = _T("%02u %s %02u: %s/%s");
|
|
UINT nPos = 0;
|
|
m_CbnInstrument.SetRedraw(FALSE);
|
|
m_CbnInstrument.ResetContent();
|
|
m_CbnInstrument.SetItemData(m_CbnInstrument.AddString(_T(" No Instrument")), 0);
|
|
const INSTRUMENTINDEX nSplitIns = m_modDoc.GetSplitKeyboardSettings().splitInstrument;
|
|
const ModCommand::NOTE noteSplit = 1 + m_modDoc.GetSplitKeyboardSettings().splitNote;
|
|
const CString sSplitInsName = m_modDoc.GetPatternViewInstrumentName(nSplitIns, true, false);
|
|
if(m_sndFile.GetNumInstruments())
|
|
{
|
|
// Show instrument names
|
|
for(INSTRUMENTINDEX i = 1; i <= m_sndFile.GetNumInstruments(); i++)
|
|
{
|
|
if(m_sndFile.Instruments[i] == nullptr)
|
|
continue;
|
|
|
|
CString sDisplayName;
|
|
if(m_modDoc.GetSplitKeyboardSettings().IsSplitActive())
|
|
{
|
|
s.Format(szSplitFormat,
|
|
nSplitIns,
|
|
mpt::ToCString(m_sndFile.GetNoteName(noteSplit, nSplitIns)).GetString(),
|
|
i,
|
|
sSplitInsName.GetString(),
|
|
m_modDoc.GetPatternViewInstrumentName(i, true, false).GetString());
|
|
sDisplayName = s;
|
|
}
|
|
else
|
|
sDisplayName = m_modDoc.GetPatternViewInstrumentName(i);
|
|
|
|
UINT n = m_CbnInstrument.AddString(sDisplayName);
|
|
if(n == m_nInstrument) nPos = n;
|
|
m_CbnInstrument.SetItemData(n, i);
|
|
}
|
|
} else
|
|
{
|
|
// Show sample names
|
|
SAMPLEINDEX nmax = m_sndFile.GetNumSamples();
|
|
for(SAMPLEINDEX i = 1; i <= nmax; i++) if (m_sndFile.GetSample(i).HasSampleData() || m_sndFile.GetSample(i).uFlags[CHN_ADLIB])
|
|
{
|
|
if (m_modDoc.GetSplitKeyboardSettings().IsSplitActive())
|
|
s.Format(szSplitFormat,
|
|
nSplitIns,
|
|
mpt::ToCString(m_sndFile.GetNoteName(noteSplit, nSplitIns)).GetString(),
|
|
i,
|
|
mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.m_szNames[nSplitIns]).GetString(),
|
|
mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.m_szNames[i]).GetString());
|
|
else
|
|
s.Format(_T("%02u: %s"),
|
|
i,
|
|
mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.m_szNames[i]).GetString());
|
|
|
|
UINT n = m_CbnInstrument.AddString(s);
|
|
if(n == m_nInstrument) nPos = n;
|
|
m_CbnInstrument.SetItemData(n, i);
|
|
}
|
|
}
|
|
m_CbnInstrument.SetCurSel(nPos);
|
|
m_CbnInstrument.SetRedraw(TRUE);
|
|
m_CbnInstrument.Invalidate(FALSE);
|
|
}
|
|
if(updateAll || updatePatNames)
|
|
{
|
|
PATTERNINDEX nPat;
|
|
if(patternHint.GetType()[HINT_PATNAMES])
|
|
nPat = patternHint.GetPattern();
|
|
else
|
|
nPat = (PATTERNINDEX)SendViewMessage(VIEWMSG_GETCURRENTPATTERN);
|
|
if(m_sndFile.Patterns.IsValidIndex(nPat))
|
|
{
|
|
m_EditPatName.SetWindowText(mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.Patterns[nPat].GetName()));
|
|
}
|
|
|
|
BOOL bXMIT = (m_sndFile.GetType() & (MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT)) ? TRUE : FALSE;
|
|
m_ToolBar.EnableButton(ID_PATTERN_MIDIMACRO, bXMIT);
|
|
m_ToolBar.EnableButton(ID_PATTERN_PROPERTIES, bXMIT);
|
|
m_ToolBar.EnableButton(ID_PATTERN_EXPAND, bXMIT);
|
|
m_ToolBar.EnableButton(ID_PATTERN_SHRINK, bXMIT);
|
|
}
|
|
UnlockControls();
|
|
}
|
|
if(hintType[HINT_MODTYPE | HINT_UNDO])
|
|
{
|
|
m_ToolBar.EnableButton(ID_EDIT_UNDO, m_modDoc.GetPatternUndo().CanUndo());
|
|
}
|
|
}
|
|
|
|
|
|
CRuntimeClass *CCtrlPatterns::GetAssociatedViewClass()
|
|
{
|
|
return RUNTIME_CLASS(CViewPattern);
|
|
}
|
|
|
|
|
|
LRESULT CCtrlPatterns::OnModCtrlMsg(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(wParam)
|
|
{
|
|
case CTRLMSG_GETCURRENTINSTRUMENT:
|
|
return m_nInstrument;
|
|
|
|
case CTRLMSG_GETCURRENTPATTERN:
|
|
return m_OrderList.GetCurrentPattern();
|
|
|
|
case CTRLMSG_PATTERNCHANGED:
|
|
UpdateView(PatternHint(static_cast<PATTERNINDEX>(lParam)).Names());
|
|
break;
|
|
|
|
case CTRLMSG_PAT_PREVINSTRUMENT:
|
|
OnPrevInstrument();
|
|
break;
|
|
|
|
case CTRLMSG_PAT_NEXTINSTRUMENT:
|
|
OnNextInstrument();
|
|
break;
|
|
|
|
case CTRLMSG_NOTIFYCURRENTORDER:
|
|
if(m_OrderList.GetCurSel().GetSelCount() > 1 || m_OrderList.m_bDragging)
|
|
{
|
|
// Only update play cursor in case there's a selection
|
|
m_OrderList.Invalidate(FALSE);
|
|
break;
|
|
}
|
|
// Otherwise, just act the same as a normal selection change
|
|
[[fallthrough]];
|
|
case CTRLMSG_SETCURRENTORDER:
|
|
// Set order list selection and refresh GUI if change successful
|
|
m_OrderList.SetCurSel(static_cast<ORDERINDEX>(lParam), false, false, true);
|
|
break;
|
|
|
|
case CTRLMSG_FORCEREFRESH:
|
|
//refresh GUI
|
|
m_OrderList.InvalidateRect(NULL, FALSE);
|
|
break;
|
|
|
|
case CTRLMSG_GETCURRENTORDER:
|
|
return m_OrderList.GetCurSel(true).firstOrd;
|
|
|
|
case CTRLMSG_SETCURRENTINSTRUMENT:
|
|
case CTRLMSG_PAT_SETINSTRUMENT:
|
|
return SetCurrentInstrument(static_cast<uint32>(lParam));
|
|
|
|
case CTRLMSG_SETVIEWWND:
|
|
{
|
|
SendViewMessage(VIEWMSG_FOLLOWSONG, IsDlgButtonChecked(IDC_PATTERN_FOLLOWSONG));
|
|
SendViewMessage(VIEWMSG_PATTERNLOOP, (m_sndFile.m_SongFlags & SONG_PATTERNLOOP) ? TRUE : FALSE);
|
|
OnSpacingChanged();
|
|
SendViewMessage(VIEWMSG_SETDETAIL, m_nDetailLevel);
|
|
SendViewMessage(VIEWMSG_SETRECORD, m_bRecord);
|
|
SendViewMessage(VIEWMSG_SETVUMETERS, m_bVUMeters);
|
|
SendViewMessage(VIEWMSG_SETPLUGINNAMES, m_bPluginNames);
|
|
}
|
|
break;
|
|
|
|
case CTRLMSG_SETSPACING:
|
|
SetDlgItemInt(IDC_EDIT_SPACING, static_cast<UINT>(lParam));
|
|
break;
|
|
|
|
case CTRLMSG_SETFOCUS:
|
|
GetParentFrame()->SetActiveView(&m_parent);
|
|
m_OrderList.SetFocus();
|
|
break;
|
|
|
|
case CTRLMSG_SETRECORD:
|
|
if (lParam >= 0) m_bRecord = (BOOL)(lParam); else m_bRecord = !m_bRecord;
|
|
m_ToolBar.SetState(IDC_PATTERN_RECORD, ((m_bRecord) ? TBSTATE_CHECKED : 0)|TBSTATE_ENABLED);
|
|
TrackerSettings::Instance().gbPatternRecord = (m_bRecord != 0);
|
|
SendViewMessage(VIEWMSG_SETRECORD, m_bRecord);
|
|
break;
|
|
|
|
case CTRLMSG_PREVORDER:
|
|
m_OrderList.SetCurSel(Order().GetPreviousOrderIgnoringSkips(m_OrderList.GetCurSel(true).firstOrd), true);
|
|
break;
|
|
|
|
case CTRLMSG_NEXTORDER:
|
|
m_OrderList.SetCurSel(Order().GetNextOrderIgnoringSkips(m_OrderList.GetCurSel(true).firstOrd), true);
|
|
break;
|
|
|
|
//rewbs.customKeys
|
|
case CTRLMSG_PAT_FOLLOWSONG:
|
|
// parameters: 0 = turn off, 1 = toggle
|
|
{
|
|
UINT state = FALSE;
|
|
if(lParam == 1) // toggle
|
|
{
|
|
state = !IsDlgButtonChecked(IDC_PATTERN_FOLLOWSONG);
|
|
}
|
|
CheckDlgButton(IDC_PATTERN_FOLLOWSONG, state);
|
|
OnFollowSong();
|
|
}
|
|
break;
|
|
|
|
case CTRLMSG_PAT_LOOP:
|
|
{
|
|
bool setLoop = false;
|
|
if (lParam == -1)
|
|
{
|
|
//Toggle loop state
|
|
setLoop = !m_sndFile.m_SongFlags[SONG_PATTERNLOOP];
|
|
} else
|
|
{
|
|
setLoop = (lParam != 0);
|
|
}
|
|
|
|
m_sndFile.m_SongFlags.set(SONG_PATTERNLOOP, setLoop);
|
|
CheckDlgButton(IDC_PATTERN_LOOP, setLoop ? BST_CHECKED : BST_UNCHECKED);
|
|
break;
|
|
}
|
|
case CTRLMSG_PAT_NEWPATTERN:
|
|
OnPatternNew();
|
|
break;
|
|
|
|
case CTRLMSG_PAT_DUPPATTERN:
|
|
OnPatternDuplicate();
|
|
break;
|
|
|
|
case CTRLMSG_PAT_SETSEQUENCE:
|
|
m_OrderList.SelectSequence(static_cast<SEQUENCEINDEX>(lParam));
|
|
UpdateView(SequenceHint(static_cast<SEQUENCEINDEX>(lParam)).Names(), nullptr);
|
|
break;
|
|
|
|
default:
|
|
return CModControlDlg::OnModCtrlMsg(wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::SetCurrentPattern(PATTERNINDEX nPat)
|
|
{
|
|
SendViewMessage(VIEWMSG_SETCURRENTPATTERN, (LPARAM)nPat);
|
|
}
|
|
|
|
|
|
BOOL CCtrlPatterns::SetCurrentInstrument(UINT nIns)
|
|
{
|
|
if(nIns == m_nInstrument)
|
|
return TRUE;
|
|
int n = m_CbnInstrument.GetCount();
|
|
for(int i = 0; i < n; i++)
|
|
{
|
|
if(m_CbnInstrument.GetItemData(i) == nIns)
|
|
{
|
|
m_CbnInstrument.SetCurSel(i);
|
|
m_nInstrument = static_cast<INSTRUMENTINDEX>(nIns);
|
|
GetDlgItem(IDC_PATINSTROPLUGGUI)->EnableWindow(HasValidPlug(m_nInstrument) ? TRUE : FALSE);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// CCtrlPatterns messages
|
|
|
|
void CCtrlPatterns::OnActivatePage(LPARAM lParam)
|
|
{
|
|
int nIns = m_parent.GetInstrumentChange();
|
|
if(nIns > 0)
|
|
{
|
|
SetCurrentInstrument(nIns);
|
|
}
|
|
|
|
if(!(lParam & 0x80000000))
|
|
{
|
|
// Pattern item
|
|
auto pat = static_cast<PATTERNINDEX>(lParam & 0xFFFF);
|
|
if(m_sndFile.Patterns.IsValidIndex(pat))
|
|
{
|
|
for(SEQUENCEINDEX seq = 0; seq < m_sndFile.Order.GetNumSequences(); seq++)
|
|
{
|
|
if(ORDERINDEX ord = m_sndFile.Order(seq).FindOrder(pat); ord != ORDERINDEX_INVALID)
|
|
{
|
|
m_OrderList.SelectSequence(seq);
|
|
m_OrderList.SetCurSel(ord, true);
|
|
UpdateView(SequenceHint(seq).Names(), nullptr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
SetCurrentPattern(pat);
|
|
} else if((lParam & 0x80000000))
|
|
{
|
|
// Order item
|
|
auto ord = static_cast<ORDERINDEX>(lParam & 0xFFFF);
|
|
auto seq = static_cast<SEQUENCEINDEX>((lParam >> 16) & 0x7FFF);
|
|
if(seq < m_sndFile.Order.GetNumSequences())
|
|
{
|
|
m_OrderList.SelectSequence(seq);
|
|
const auto &order = Order();
|
|
if(ord < order.size())
|
|
{
|
|
m_OrderList.SetCurSel(ord);
|
|
SetCurrentPattern(order[ord]);
|
|
}
|
|
UpdateView(SequenceHint(static_cast<SEQUENCEINDEX>(seq)).Names(), nullptr);
|
|
}
|
|
}
|
|
if(m_hWndView)
|
|
{
|
|
OnSpacingChanged();
|
|
if(m_bRecord)
|
|
SendViewMessage(VIEWMSG_SETRECORD, m_bRecord);
|
|
CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
|
|
|
|
// Restore all save pattern state, except pattern number which we might have just set.
|
|
PATTERNVIEWSTATE &patternViewState = pFrame->GetPatternViewState();
|
|
if(patternViewState.initialOrder != ORDERINDEX_INVALID)
|
|
{
|
|
if(CMainFrame::GetMainFrame()->GetModPlaying() != &m_modDoc)
|
|
m_OrderList.SetCurSel(patternViewState.initialOrder);
|
|
patternViewState.initialOrder = ORDERINDEX_INVALID;
|
|
}
|
|
|
|
patternViewState.nPattern = static_cast<PATTERNINDEX>(SendViewMessage(VIEWMSG_GETCURRENTPATTERN));
|
|
SendViewMessage(VIEWMSG_LOADSTATE, (LPARAM)&patternViewState);
|
|
|
|
SwitchToView();
|
|
}
|
|
|
|
// Combo boxes randomly disappear without this... why?
|
|
Invalidate();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnDeactivatePage()
|
|
{
|
|
CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
|
|
if((pFrame) && (m_hWndView))
|
|
SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&pFrame->GetPatternViewState());
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
|
|
{
|
|
CModControlDlg::OnVScroll(nSBCode, nPos, pScrollBar);
|
|
short int pos = (short int)m_SpinInstrument.GetPos();
|
|
if(pos)
|
|
{
|
|
m_SpinInstrument.SetPos(0);
|
|
if(pos < 0)
|
|
OnPrevInstrument();
|
|
else
|
|
OnNextInstrument();
|
|
}
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnSequencePrev()
|
|
{
|
|
m_OrderList.SetCurSel(m_OrderList.GetCurSel(true).firstOrd - 1);
|
|
m_OrderList.SetFocus();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnSequenceNext()
|
|
{
|
|
m_OrderList.SetCurSel(m_OrderList.GetCurSel(true).firstOrd + 1);
|
|
m_OrderList.SetFocus();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnChannelManager()
|
|
{
|
|
m_modDoc.OnChannelManager();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
|
|
{
|
|
CModControlDlg::OnKeyDown(nChar, nRepCnt, nFlags);
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnSpacingChanged()
|
|
{
|
|
if((m_EditSpacing.m_hWnd) && (m_EditSpacing.GetWindowTextLength() > 0))
|
|
{
|
|
TrackerSettings::Instance().gnPatternSpacing = GetDlgItemInt(IDC_EDIT_SPACING);
|
|
if(TrackerSettings::Instance().gnPatternSpacing > MAX_SPACING)
|
|
{
|
|
TrackerSettings::Instance().gnPatternSpacing = MAX_SPACING;
|
|
SetDlgItemInt(IDC_EDIT_SPACING, TrackerSettings::Instance().gnPatternSpacing, FALSE);
|
|
}
|
|
SendViewMessage(VIEWMSG_SETSPACING, TrackerSettings::Instance().gnPatternSpacing);
|
|
}
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnInstrumentChanged()
|
|
{
|
|
int n = m_CbnInstrument.GetCurSel();
|
|
if(n >= 0)
|
|
{
|
|
n = static_cast<int>(m_CbnInstrument.GetItemData(n));
|
|
int nmax = (m_sndFile.m_nInstruments) ? m_sndFile.m_nInstruments : m_sndFile.m_nSamples;
|
|
if((n >= 0) && (n <= nmax) && (n != (int)m_nInstrument))
|
|
{
|
|
m_nInstrument = static_cast<INSTRUMENTINDEX>(n);
|
|
m_parent.InstrumentChanged(m_nInstrument);
|
|
}
|
|
SwitchToView();
|
|
::EnableWindow(::GetDlgItem(m_hWnd, IDC_PATINSTROPLUGGUI), HasValidPlug(m_nInstrument));
|
|
}
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPrevInstrument()
|
|
{
|
|
int n = m_CbnInstrument.GetCount();
|
|
if(n > 0)
|
|
{
|
|
int pos = m_CbnInstrument.GetCurSel();
|
|
if(pos > 0)
|
|
pos--;
|
|
else
|
|
pos = n - 1;
|
|
m_CbnInstrument.SetCurSel(pos);
|
|
OnInstrumentChanged();
|
|
}
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnNextInstrument()
|
|
{
|
|
int n = m_CbnInstrument.GetCount();
|
|
if(n > 0)
|
|
{
|
|
int pos = m_CbnInstrument.GetCurSel() + 1;
|
|
if(pos >= n)
|
|
pos = 0;
|
|
m_CbnInstrument.SetCurSel(pos);
|
|
OnInstrumentChanged();
|
|
}
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPlayerPause()
|
|
{
|
|
CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
|
|
if(pMainFrm)
|
|
pMainFrm->PauseMod();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternNew()
|
|
{
|
|
const auto &order = Order();
|
|
ORDERINDEX curOrd = m_OrderList.GetCurSel(true).firstOrd;
|
|
PATTERNINDEX curPat = (curOrd < order.size()) ? order[curOrd] : 0;
|
|
ROWINDEX rows = 64;
|
|
if(m_sndFile.Patterns.IsValidPat(curPat))
|
|
{
|
|
// Only if the current oder is already occupied, create a new pattern at the next position.
|
|
curOrd++;
|
|
} else
|
|
{
|
|
// Use currently edited pattern for new pattern length
|
|
curPat = static_cast<PATTERNINDEX>(SendViewMessage(VIEWMSG_GETCURRENTPATTERN));
|
|
}
|
|
if(m_sndFile.Patterns.IsValidPat(curPat))
|
|
{
|
|
rows = m_sndFile.Patterns[curPat].GetNumRows();
|
|
}
|
|
rows = Clamp(rows, m_sndFile.GetModSpecifications().patternRowsMin, m_sndFile.GetModSpecifications().patternRowsMax);
|
|
const PATTERNINDEX newPat = m_modDoc.InsertPattern(rows, curOrd);
|
|
if(m_sndFile.Patterns.IsValidPat(newPat))
|
|
{
|
|
// update time signature
|
|
if(m_sndFile.Patterns.IsValidIndex(curPat))
|
|
{
|
|
if(m_sndFile.Patterns[curPat].GetOverrideSignature())
|
|
m_sndFile.Patterns[newPat].SetSignature(m_sndFile.Patterns[curPat].GetRowsPerBeat(), m_sndFile.Patterns[curPat].GetRowsPerMeasure());
|
|
if(m_sndFile.Patterns[curPat].HasTempoSwing())
|
|
m_sndFile.Patterns[newPat].SetTempoSwing(m_sndFile.Patterns[curPat].GetTempoSwing());
|
|
}
|
|
// move to new pattern
|
|
m_OrderList.SetCurSel(curOrd);
|
|
m_OrderList.Invalidate(FALSE);
|
|
SetCurrentPattern(newPat);
|
|
m_modDoc.SetModified();
|
|
m_modDoc.UpdateAllViews(NULL, PatternHint(newPat).Names(), this);
|
|
m_modDoc.UpdateAllViews(NULL, SequenceHint().Data(), this);
|
|
SwitchToView();
|
|
}
|
|
}
|
|
|
|
|
|
// Duplicates one or more patterns.
|
|
void CCtrlPatterns::OnPatternDuplicate()
|
|
{
|
|
OrdSelection selection = m_OrderList.GetCurSel();
|
|
const ORDERINDEX insertFrom = selection.firstOrd;
|
|
const ORDERINDEX insertWhere = selection.lastOrd + 1u;
|
|
if(insertWhere >= m_sndFile.GetModSpecifications().ordersMax)
|
|
return;
|
|
const ORDERINDEX insertCount = std::min(selection.GetSelCount(), static_cast<ORDERINDEX>(m_sndFile.GetModSpecifications().ordersMax - insertWhere));
|
|
if(!insertCount)
|
|
return;
|
|
|
|
bool success = false, outOfPatterns = false;
|
|
// Has this pattern been duplicated already? (for multiselect)
|
|
std::vector<PATTERNINDEX> patReplaceIndex(m_sndFile.Patterns.Size(), PATTERNINDEX_INVALID);
|
|
|
|
ModSequence &order = Order();
|
|
for(ORDERINDEX i = 0; i < insertCount; i++)
|
|
{
|
|
PATTERNINDEX curPat = order[insertFrom + i];
|
|
if(curPat < patReplaceIndex.size() && patReplaceIndex[curPat] == PATTERNINDEX_INVALID)
|
|
{
|
|
PATTERNINDEX newPat = m_sndFile.Patterns.Duplicate(curPat, true);
|
|
if(newPat != PATTERNINDEX_INVALID)
|
|
{
|
|
order.insert(insertWhere + i, 1, newPat);
|
|
success = true;
|
|
// Mark as duplicated, so if this pattern is to be duplicated again, the same new pattern number is inserted into the order list.
|
|
patReplaceIndex[curPat] = newPat;
|
|
} else
|
|
{
|
|
if(m_sndFile.Patterns.IsValidPat(curPat))
|
|
outOfPatterns = true;
|
|
continue;
|
|
}
|
|
} else
|
|
{
|
|
// Invalid pattern, or it has been duplicated before (multiselect)
|
|
PATTERNINDEX newPat;
|
|
if(curPat < patReplaceIndex.size() && patReplaceIndex[curPat] != PATTERNINDEX_INVALID)
|
|
{
|
|
// Take care of patterns that have been duplicated before
|
|
newPat = patReplaceIndex[curPat];
|
|
} else
|
|
{
|
|
newPat = order[insertFrom + i];
|
|
}
|
|
|
|
order.insert(insertWhere + i, 1, newPat);
|
|
|
|
success = true;
|
|
}
|
|
}
|
|
if(success)
|
|
{
|
|
m_OrderList.InsertUpdatePlaystate(selection.firstOrd, selection.lastOrd);
|
|
|
|
m_OrderList.Invalidate(FALSE);
|
|
m_OrderList.SetCurSel(insertWhere, true, false, true);
|
|
|
|
// If the first duplicated order is e.g. a +++ item, we need to move the pattern display on or else we'll still edit the previously shown pattern.
|
|
ORDERINDEX showPattern = std::min(insertWhere, order.GetLastIndex());
|
|
while(!order.IsValidPat(showPattern) && showPattern < order.GetLastIndex())
|
|
{
|
|
showPattern++;
|
|
}
|
|
SetCurrentPattern(order[showPattern]);
|
|
|
|
m_modDoc.SetModified();
|
|
m_modDoc.UpdateAllViews(nullptr, SequenceHint().Data(), this);
|
|
m_modDoc.UpdateAllViews(nullptr, PatternHint(PATTERNINDEX_INVALID).Names(), this);
|
|
if(selection.lastOrd != selection.firstOrd)
|
|
m_OrderList.m_nScrollPos2nd = insertWhere + insertCount - 1u;
|
|
}
|
|
if(outOfPatterns)
|
|
{
|
|
const auto &specs = m_sndFile.GetModSpecifications();
|
|
Reporting::Error(MPT_AFORMAT("Pattern limit of the {} format ({} patterns) has been reached.")(mpt::ToUpperCaseAscii(specs.fileExtension), specs.patternsMax), "Duplicate Patterns");
|
|
}
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
// Merges one or more patterns into a single pattern
|
|
void CCtrlPatterns::OnPatternMerge()
|
|
{
|
|
const OrdSelection selection = m_OrderList.GetCurSel();
|
|
const ORDERINDEX firstOrder = selection.firstOrd;
|
|
const ORDERINDEX numOrders = selection.GetSelCount();
|
|
|
|
// Get the total number of lines to be merged
|
|
ROWINDEX numRows = 0u;
|
|
ModSequence &order = Order();
|
|
for(ORDERINDEX i = 0; i < numOrders; i++)
|
|
{
|
|
PATTERNINDEX pat = order[firstOrder + i];
|
|
if(m_sndFile.Patterns.IsValidPat(pat))
|
|
numRows += m_sndFile.Patterns[pat].GetNumRows();
|
|
}
|
|
if(!numRows || numOrders < 2)
|
|
{
|
|
MessageBeep(MB_ICONWARNING);
|
|
SwitchToView();
|
|
return;
|
|
}
|
|
|
|
// Try to create a new pattern for the merge
|
|
const auto &specs = m_sndFile.GetModSpecifications();
|
|
const auto format = mpt::ToUpperCaseAscii(specs.fileExtension);
|
|
if(numRows > specs.patternRowsMax)
|
|
{
|
|
Reporting::Error(MPT_AFORMAT("Merged pattern size ({} rows) exceeds the row limit ({} rows) of the {} format.")(numRows, specs.patternRowsMax, format), "Merge Patterns");
|
|
SwitchToView();
|
|
return;
|
|
}
|
|
|
|
CriticalSection cs;
|
|
const PATTERNINDEX newPat = m_sndFile.Patterns.InsertAny(std::max(numRows, specs.patternRowsMin), true);
|
|
if(newPat == PATTERNINDEX_INVALID)
|
|
{
|
|
cs.Leave();
|
|
Reporting::Error(MPT_AFORMAT("Pattern limit of the {} format ({} patterns) has been reached.")(format, specs.patternsMax), "Merge Patterns");
|
|
SwitchToView();
|
|
return;
|
|
}
|
|
|
|
auto &pattern = m_sndFile.Patterns[newPat];
|
|
auto it = pattern.begin();
|
|
for(ORDERINDEX i = 0; i < numOrders; i++)
|
|
{
|
|
PATTERNINDEX pat = order[firstOrder + i];
|
|
if(m_sndFile.Patterns.IsValidPat(pat))
|
|
it = std::copy(m_sndFile.Patterns[pat].begin(), m_sndFile.Patterns[pat].end(), it);
|
|
}
|
|
|
|
if(pattern.GetNumRows() > numRows)
|
|
pattern.WriteEffect(EffectWriter(CMD_PATTERNBREAK, 0).Row(numRows - 1).RetryNextRow());
|
|
|
|
// Remove the merged patterns...
|
|
order.Remove(selection.firstOrd, selection.lastOrd);
|
|
m_OrderList.DeleteUpdatePlaystate(selection.firstOrd, selection.lastOrd);
|
|
// ...and insert the new one
|
|
order.insert(firstOrder, 1, newPat);
|
|
m_OrderList.InsertUpdatePlaystate(firstOrder, firstOrder);
|
|
|
|
m_OrderList.Invalidate(FALSE);
|
|
m_OrderList.SetSelection(firstOrder);
|
|
SetCurrentPattern(newPat);
|
|
|
|
m_modDoc.SetModified();
|
|
m_modDoc.UpdateAllViews(nullptr, SequenceHint().Data(), this);
|
|
m_modDoc.UpdateAllViews(nullptr, PatternHint(PATTERNINDEX_INVALID).Names(), this);
|
|
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternStop()
|
|
{
|
|
CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
|
|
if(pMainFrm)
|
|
pMainFrm->PauseMod(&m_modDoc);
|
|
m_sndFile.ResetChannels();
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternPlay()
|
|
{
|
|
m_modDoc.OnPatternPlay();
|
|
SwitchToView();
|
|
}
|
|
|
|
//rewbs.playSongFromCursor
|
|
void CCtrlPatterns::OnPatternPlayNoLoop()
|
|
{
|
|
m_modDoc.OnPatternPlayNoLoop();
|
|
SwitchToView();
|
|
}
|
|
//end rewbs.playSongFromCursor
|
|
|
|
void CCtrlPatterns::OnPatternPlayFromStart()
|
|
{
|
|
m_modDoc.OnPatternRestart();
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternRecord()
|
|
{
|
|
UINT nState = m_ToolBar.GetState(IDC_PATTERN_RECORD);
|
|
m_bRecord = ((nState & TBSTATE_CHECKED) != 0);
|
|
TrackerSettings::Instance().gbPatternRecord = (m_bRecord != 0);
|
|
SendViewMessage(VIEWMSG_SETRECORD, m_bRecord);
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternVUMeters()
|
|
{
|
|
UINT nState = m_ToolBar.GetState(ID_PATTERN_VUMETERS);
|
|
m_bVUMeters = ((nState & TBSTATE_CHECKED) != 0);
|
|
TrackerSettings::Instance().gbPatternVUMeters = (m_bVUMeters != 0);
|
|
SendViewMessage(VIEWMSG_SETVUMETERS, m_bVUMeters);
|
|
SwitchToView();
|
|
}
|
|
|
|
//rewbs.patPlugName
|
|
void CCtrlPatterns::OnPatternViewPlugNames()
|
|
{
|
|
UINT nState = m_ToolBar.GetState(ID_VIEWPLUGNAMES);
|
|
m_bPluginNames = ((nState & TBSTATE_CHECKED) != 0);
|
|
TrackerSettings::Instance().gbPatternPluginNames = (m_bPluginNames != 0);
|
|
SendViewMessage(VIEWMSG_SETPLUGINNAMES, m_bPluginNames);
|
|
SwitchToView();
|
|
}
|
|
//end rewbs.patPlugName
|
|
|
|
void CCtrlPatterns::OnPatternProperties()
|
|
{
|
|
SendViewMessage(VIEWMSG_PATTERNPROPERTIES, PATTERNINDEX_INVALID);
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternExpand()
|
|
{
|
|
SendViewMessage(VIEWMSG_EXPANDPATTERN);
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternCopy()
|
|
{
|
|
SendViewMessage(VIEWMSG_COPYPATTERN);
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternPaste()
|
|
{
|
|
SendViewMessage(VIEWMSG_PASTEPATTERN);
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternShrink()
|
|
{
|
|
SendViewMessage(VIEWMSG_SHRINKPATTERN);
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternAmplify()
|
|
{
|
|
SendViewMessage(VIEWMSG_AMPLIFYPATTERN);
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternPlayRow()
|
|
{
|
|
::SendMessage(m_hWndView, WM_COMMAND, ID_PATTERN_PLAYROW, 0);
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnUpdateRecord(CCmdUI *pCmdUI)
|
|
{
|
|
if(pCmdUI)
|
|
pCmdUI->SetCheck((m_bRecord) ? TRUE : FALSE);
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnFollowSong()
|
|
{
|
|
SendViewMessage(VIEWMSG_FOLLOWSONG, IsDlgButtonChecked(IDC_PATTERN_FOLLOWSONG));
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnChangeLoopStatus()
|
|
{
|
|
OnModCtrlMsg(CTRLMSG_PAT_LOOP, IsDlgButtonChecked(IDC_PATTERN_LOOP));
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnEditUndo()
|
|
{
|
|
if(m_hWndView)
|
|
::SendMessage(m_hWndView, WM_COMMAND, ID_EDIT_UNDO, 0);
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnSwitchToView()
|
|
{
|
|
PostViewMessage(VIEWMSG_SETFOCUS);
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnPatternNameChanged()
|
|
{
|
|
if(!IsLocked())
|
|
{
|
|
const PATTERNINDEX nPat = (PATTERNINDEX)SendViewMessage(VIEWMSG_GETCURRENTPATTERN);
|
|
|
|
CString tmp;
|
|
m_EditPatName.GetWindowText(tmp);
|
|
const std::string s = mpt::ToCharset(m_sndFile.GetCharsetInternal(), tmp);
|
|
|
|
if(m_sndFile.Patterns[nPat].GetName() != s)
|
|
{
|
|
if(m_sndFile.Patterns[nPat].SetName(s))
|
|
{
|
|
if(m_sndFile.GetType() & (MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT))
|
|
m_modDoc.SetModified();
|
|
m_modDoc.UpdateAllViews(NULL, PatternHint(nPat).Names(), this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnSequenceNameChanged()
|
|
{
|
|
CString tmp;
|
|
GetDlgItemText(IDC_EDIT_SEQUENCE_NAME, tmp);
|
|
const mpt::ustring str = mpt::ToUnicode(tmp);
|
|
auto &order = Order();
|
|
if(str != order.GetName())
|
|
{
|
|
order.SetName(str);
|
|
m_modDoc.SetModified();
|
|
m_modDoc.UpdateAllViews(nullptr, SequenceHint(m_sndFile.Order.GetCurrentSequenceIndex()).Names(), this);
|
|
}
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnChordEditor()
|
|
{
|
|
CChordEditor dlg(this);
|
|
dlg.DoModal();
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnDetailLo()
|
|
{
|
|
m_ToolBar.SetState(ID_PATTERNDETAIL_LO, TBSTATE_CHECKED | TBSTATE_ENABLED);
|
|
if(m_nDetailLevel != PatternCursor::instrColumn)
|
|
{
|
|
m_nDetailLevel = PatternCursor::instrColumn;
|
|
m_ToolBar.SetState(ID_PATTERNDETAIL_MED, TBSTATE_ENABLED);
|
|
m_ToolBar.SetState(ID_PATTERNDETAIL_HI, TBSTATE_ENABLED);
|
|
SendViewMessage(VIEWMSG_SETDETAIL, m_nDetailLevel);
|
|
}
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnDetailMed()
|
|
{
|
|
m_ToolBar.SetState(ID_PATTERNDETAIL_MED, TBSTATE_CHECKED | TBSTATE_ENABLED);
|
|
if(m_nDetailLevel != PatternCursor::volumeColumn)
|
|
{
|
|
m_nDetailLevel = PatternCursor::volumeColumn;
|
|
m_ToolBar.SetState(ID_PATTERNDETAIL_LO, TBSTATE_ENABLED);
|
|
m_ToolBar.SetState(ID_PATTERNDETAIL_HI, TBSTATE_ENABLED);
|
|
SendViewMessage(VIEWMSG_SETDETAIL, m_nDetailLevel);
|
|
}
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnDetailHi()
|
|
{
|
|
m_ToolBar.SetState(ID_PATTERNDETAIL_HI, TBSTATE_CHECKED | TBSTATE_ENABLED);
|
|
if(m_nDetailLevel != PatternCursor::lastColumn)
|
|
{
|
|
m_nDetailLevel = PatternCursor::lastColumn;
|
|
m_ToolBar.SetState(ID_PATTERNDETAIL_LO, TBSTATE_ENABLED);
|
|
m_ToolBar.SetState(ID_PATTERNDETAIL_MED, TBSTATE_ENABLED);
|
|
SendViewMessage(VIEWMSG_SETDETAIL, m_nDetailLevel);
|
|
}
|
|
SwitchToView();
|
|
}
|
|
|
|
void CCtrlPatterns::OnToggleOverflowPaste()
|
|
{
|
|
TrackerSettings::Instance().m_dwPatternSetup ^= PATTERN_OVERFLOWPASTE;
|
|
UpdateView(UpdateHint().MPTOptions());
|
|
SwitchToView();
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::TogglePluginEditor()
|
|
{
|
|
if(m_sndFile.GetInstrumentPlugin(m_nInstrument) != nullptr)
|
|
{
|
|
m_modDoc.TogglePluginEditor(m_sndFile.Instruments[m_nInstrument]->nMixPlug - 1, CMainFrame::GetInputHandler()->ShiftPressed());
|
|
}
|
|
}
|
|
|
|
|
|
bool CCtrlPatterns::HasValidPlug(INSTRUMENTINDEX instr) const
|
|
{
|
|
return m_sndFile.GetInstrumentPlugin(instr) != nullptr;
|
|
}
|
|
|
|
|
|
BOOL CCtrlPatterns::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
|
|
{
|
|
if(nFlags == 0)
|
|
{
|
|
PostViewMessage(VIEWMSG_DOSCROLL, zDelta);
|
|
}
|
|
return CModControlDlg::OnMouseWheel(nFlags, zDelta, pt);
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnXButtonUp(UINT nFlags, UINT nButton, CPoint point)
|
|
{
|
|
if(nButton == XBUTTON1)
|
|
OnModCtrlMsg(CTRLMSG_PREVORDER, 0);
|
|
else if(nButton == XBUTTON2)
|
|
OnModCtrlMsg(CTRLMSG_NEXTORDER, 0);
|
|
CModControlDlg::OnXButtonUp(nFlags, nButton, point);
|
|
}
|
|
|
|
|
|
BOOL CCtrlPatterns::OnToolTip(UINT /*id*/, NMHDR *pNMHDR, LRESULT * /*pResult*/)
|
|
{
|
|
TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
|
|
UINT_PTR nID = pNMHDR->idFrom;
|
|
if(pTTT->uFlags & TTF_IDISHWND)
|
|
{
|
|
// idFrom is actually the HWND of the tool
|
|
nID = ::GetDlgCtrlID((HWND)nID);
|
|
if(nID)
|
|
{
|
|
pTTT->lpszText = MAKEINTRESOURCE(nID);
|
|
pTTT->hinst = AfxGetResourceHandle();
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL CCtrlPatterns::GetToolTipText(UINT id, LPTSTR str)
|
|
{
|
|
CString fmt;
|
|
const TCHAR *s = nullptr;
|
|
CommandID cmd = kcNull;
|
|
switch(id)
|
|
{
|
|
case IDC_PATTERN_NEW: s = _T("Insert Pattern"); cmd = kcNewPattern; break;
|
|
case IDC_PATTERN_PLAY: s = _T("Play Pattern"); cmd = kcPlayPatternFromCursor; break;
|
|
case IDC_PATTERN_PLAYFROMSTART: s = _T("Replay Pattern"); cmd = kcPlayPatternFromStart; break;
|
|
case IDC_PATTERN_STOP: s = _T("Stop"); cmd = kcPauseSong; break;
|
|
case ID_PATTERN_PLAYROW: s = _T("Play Row"); cmd = kcPatternPlayRow; break;
|
|
case IDC_PATTERN_RECORD: s = _T("Record"); cmd = kcPatternRecord; break;
|
|
case ID_PATTERN_VUMETERS: s = _T("VU-Meters"); break;
|
|
case ID_VIEWPLUGNAMES: s = _T("Show Plugins"); break;
|
|
case ID_PATTERN_CHANNELMANAGER: s = _T("Channel Manager"); cmd = kcViewChannelManager; break;
|
|
case ID_PATTERN_MIDIMACRO: s = _T("Zxx Macro Configuration"); cmd = kcShowMacroConfig; break;
|
|
case ID_PATTERN_CHORDEDIT: s = _T("Chord Editor"); cmd = kcChordEditor; break;
|
|
case ID_EDIT_UNDO:
|
|
fmt = _T("Undo");
|
|
if(m_modDoc.GetPatternUndo().CanUndo())
|
|
fmt += _T(" ") + m_modDoc.GetPatternUndo().GetUndoName();
|
|
cmd = kcEditUndo;
|
|
break;
|
|
case ID_PATTERN_PROPERTIES: s = _T("Pattern Properties"); cmd = kcShowPatternProperties; break;
|
|
case ID_PATTERN_EXPAND: s = _T("Expand Pattern"); break;
|
|
case ID_PATTERN_SHRINK: s = _T("Shrink Pattern"); break;
|
|
case ID_PATTERNDETAIL_LO: s = _T("Low Pattern Detail Level"); break;
|
|
case ID_PATTERNDETAIL_MED: s = _T("Medium Pattern Detail Level"); break;
|
|
case ID_PATTERNDETAIL_HI: s = _T("High Pattern Detail Level"); break;
|
|
case ID_OVERFLOWPASTE: s = _T("Toggle Overflow Paste"); cmd = kcToggleOverflowPaste; break;
|
|
case IDC_PATTERN_LOOP: s = _T("Toggle Loop Pattern"); cmd = kcChangeLoopStatus; break;
|
|
case IDC_PATTERN_FOLLOWSONG: s = _T("Toggle Follow Song"); cmd = kcToggleFollowSong; break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
if(s != nullptr)
|
|
fmt = s;
|
|
if(cmd != kcNull)
|
|
{
|
|
auto keyText = CMainFrame::GetInputHandler()->m_activeCommandSet->GetKeyTextFromCommand(cmd, 0);
|
|
if(!keyText.IsEmpty())
|
|
fmt += MPT_CFORMAT(" ({})")(keyText);
|
|
}
|
|
_tcscpy(str, fmt.GetString());
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void CCtrlPatterns::OnSequenceNumChanged()
|
|
{
|
|
if((m_EditSequence.m_hWnd) && (m_EditSequence.GetWindowTextLength() > 0))
|
|
{
|
|
SEQUENCEINDEX newSeq = static_cast<SEQUENCEINDEX>(GetDlgItemInt(IDC_EDIT_SEQNUM) - 1);
|
|
|
|
if(newSeq == m_sndFile.Order.GetCurrentSequenceIndex())
|
|
return;
|
|
|
|
if(newSeq >= m_sndFile.Order.GetNumSequences())
|
|
{
|
|
newSeq = m_sndFile.Order.GetNumSequences() - 1;
|
|
SetDlgItemInt(IDC_EDIT_SEQNUM, newSeq + 1, FALSE);
|
|
}
|
|
m_OrderList.SelectSequence(newSeq);
|
|
UpdateView(SequenceHint(newSeq).Names(), nullptr);
|
|
}
|
|
}
|
|
|
|
OPENMPT_NAMESPACE_END
|