Ctrl_pat.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302
  1. /*
  2. * Ctrl_pat.cpp
  3. * ------------
  4. * Purpose: Pattern tab, upper panel.
  5. * Notes : (currently none)
  6. * Authors: Olivier Lapicque
  7. * OpenMPT Devs
  8. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  9. */
  10. #include "stdafx.h"
  11. #include "Mptrack.h"
  12. #include "Mainfrm.h"
  13. #include "InputHandler.h"
  14. #include "ImageLists.h"
  15. #include "Childfrm.h"
  16. #include "Moddoc.h"
  17. #include "../soundlib/mod_specifications.h"
  18. #include "Globals.h"
  19. #include "Ctrl_pat.h"
  20. #include "View_pat.h"
  21. #include "PatternEditorDialogs.h"
  22. #include "ChannelManagerDlg.h"
  23. #include "../common/mptStringBuffer.h"
  24. OPENMPT_NAMESPACE_BEGIN
  25. //////////////////////////////////////////////////////////////
  26. // CCtrlPatterns
  27. BEGIN_MESSAGE_MAP(CCtrlPatterns, CModControlDlg)
  28. //{{AFX_MSG_MAP(CCtrlPatterns)
  29. ON_WM_KEYDOWN()
  30. ON_WM_VSCROLL()
  31. ON_WM_XBUTTONUP()
  32. ON_COMMAND(IDC_BUTTON1, &CCtrlPatterns::OnSequenceNext)
  33. ON_COMMAND(IDC_BUTTON2, &CCtrlPatterns::OnSequencePrev)
  34. ON_COMMAND(ID_PLAYER_PAUSE, &CCtrlPatterns::OnPlayerPause)
  35. ON_COMMAND(IDC_PATTERN_NEW, &CCtrlPatterns::OnPatternNew)
  36. ON_COMMAND(IDC_PATTERN_STOP, &CCtrlPatterns::OnPatternStop)
  37. ON_COMMAND(IDC_PATTERN_PLAY, &CCtrlPatterns::OnPatternPlay)
  38. ON_COMMAND(IDC_PATTERN_PLAYFROMSTART, &CCtrlPatterns::OnPatternPlayFromStart)
  39. ON_COMMAND(IDC_PATTERN_RECORD, &CCtrlPatterns::OnPatternRecord)
  40. ON_COMMAND(IDC_PATTERN_LOOP, &CCtrlPatterns::OnChangeLoopStatus)
  41. ON_COMMAND(ID_PATTERN_PLAYROW, &CCtrlPatterns::OnPatternPlayRow)
  42. ON_COMMAND(ID_PATTERN_CHANNELMANAGER, &CCtrlPatterns::OnChannelManager)
  43. ON_COMMAND(ID_PATTERN_VUMETERS, &CCtrlPatterns::OnPatternVUMeters)
  44. ON_COMMAND(ID_VIEWPLUGNAMES, &CCtrlPatterns::OnPatternViewPlugNames)
  45. ON_COMMAND(ID_NEXTINSTRUMENT, &CCtrlPatterns::OnNextInstrument)
  46. ON_COMMAND(ID_PREVINSTRUMENT, &CCtrlPatterns::OnPrevInstrument)
  47. ON_COMMAND(IDC_PATTERN_FOLLOWSONG, &CCtrlPatterns::OnFollowSong)
  48. ON_COMMAND(ID_PATTERN_CHORDEDIT, &CCtrlPatterns::OnChordEditor)
  49. ON_COMMAND(ID_PATTERN_PROPERTIES, &CCtrlPatterns::OnPatternProperties)
  50. ON_COMMAND(ID_PATTERN_EXPAND, &CCtrlPatterns::OnPatternExpand)
  51. ON_COMMAND(ID_PATTERN_SHRINK, &CCtrlPatterns::OnPatternShrink)
  52. ON_COMMAND(ID_PATTERN_AMPLIFY, &CCtrlPatterns::OnPatternAmplify)
  53. ON_COMMAND(ID_ORDERLIST_NEW, &CCtrlPatterns::OnPatternNew)
  54. ON_COMMAND(ID_ORDERLIST_COPY, &CCtrlPatterns::OnPatternDuplicate)
  55. ON_COMMAND(ID_ORDERLIST_MERGE, &CCtrlPatterns::OnPatternMerge)
  56. ON_COMMAND(ID_PATTERNCOPY, &CCtrlPatterns::OnPatternCopy)
  57. ON_COMMAND(ID_PATTERNPASTE, &CCtrlPatterns::OnPatternPaste)
  58. ON_COMMAND(ID_EDIT_UNDO, &CCtrlPatterns::OnEditUndo)
  59. ON_COMMAND(ID_PATTERNDETAIL_LO, &CCtrlPatterns::OnDetailLo)
  60. ON_COMMAND(ID_PATTERNDETAIL_MED, &CCtrlPatterns::OnDetailMed)
  61. ON_COMMAND(ID_PATTERNDETAIL_HI, &CCtrlPatterns::OnDetailHi)
  62. ON_COMMAND(ID_OVERFLOWPASTE, &CCtrlPatterns::OnToggleOverflowPaste)
  63. ON_CBN_SELCHANGE(IDC_COMBO_INSTRUMENT, &CCtrlPatterns::OnInstrumentChanged)
  64. ON_COMMAND(IDC_PATINSTROPLUGGUI, &CCtrlPatterns::TogglePluginEditor) //rewbs.instroVST
  65. ON_EN_CHANGE(IDC_EDIT_SPACING, &CCtrlPatterns::OnSpacingChanged)
  66. ON_EN_CHANGE(IDC_EDIT_PATTERNNAME, &CCtrlPatterns::OnPatternNameChanged)
  67. ON_EN_CHANGE(IDC_EDIT_SEQUENCE_NAME, &CCtrlPatterns::OnSequenceNameChanged)
  68. ON_EN_CHANGE(IDC_EDIT_SEQNUM, &CCtrlPatterns::OnSequenceNumChanged)
  69. ON_UPDATE_COMMAND_UI(IDC_PATTERN_RECORD,&CCtrlPatterns::OnUpdateRecord)
  70. ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &CCtrlPatterns::OnToolTipText)
  71. //}}AFX_MSG_MAP
  72. ON_WM_MOUSEWHEEL()
  73. END_MESSAGE_MAP()
  74. void CCtrlPatterns::DoDataExchange(CDataExchange *pDX)
  75. {
  76. CModControlDlg::DoDataExchange(pDX);
  77. //{{AFX_DATA_MAP(CCtrlPatterns)
  78. DDX_Control(pDX, IDC_BUTTON1, m_BtnNext);
  79. DDX_Control(pDX, IDC_BUTTON2, m_BtnPrev);
  80. DDX_Control(pDX, IDC_COMBO_INSTRUMENT, m_CbnInstrument);
  81. DDX_Control(pDX, IDC_EDIT_SPACING, m_EditSpacing);
  82. DDX_Control(pDX, IDC_EDIT_PATTERNNAME, m_EditPatName);
  83. DDX_Control(pDX, IDC_EDIT_SEQNUM, m_EditSequence);
  84. DDX_Control(pDX, IDC_SPIN_SPACING, m_SpinSpacing);
  85. DDX_Control(pDX, IDC_SPIN_INSTRUMENT, m_SpinInstrument);
  86. DDX_Control(pDX, IDC_SPIN_SEQNUM, m_SpinSequence);
  87. DDX_Control(pDX, IDC_TOOLBAR1, m_ToolBar);
  88. //}}AFX_DATA_MAP
  89. }
  90. const ModSequence &CCtrlPatterns::Order() const { return m_sndFile.Order(); }
  91. ModSequence &CCtrlPatterns::Order() { return m_sndFile.Order(); }
  92. CCtrlPatterns::CCtrlPatterns(CModControlView &parent, CModDoc &document)
  93. : CModControlDlg(parent, document), m_OrderList(*this, document)
  94. {
  95. m_bVUMeters = TrackerSettings::Instance().gbPatternVUMeters;
  96. m_bPluginNames = TrackerSettings::Instance().gbPatternPluginNames;
  97. m_bRecord = TrackerSettings::Instance().gbPatternRecord;
  98. }
  99. BOOL CCtrlPatterns::OnInitDialog()
  100. {
  101. CRect rect, rcOrderList;
  102. CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
  103. CModControlDlg::OnInitDialog();
  104. EnableToolTips();
  105. if(!pMainFrm)
  106. return TRUE;
  107. SetRedraw(FALSE);
  108. LockControls();
  109. // Order List
  110. m_BtnNext.GetWindowRect(&rect);
  111. ScreenToClient(&rect);
  112. auto margins = Util::ScalePixels(4, m_hWnd);
  113. rcOrderList.left = rect.right + margins;
  114. rcOrderList.top = rect.top;
  115. rcOrderList.bottom = rect.bottom + GetSystemMetrics(SM_CYHSCROLL);
  116. GetClientRect(&rect);
  117. rcOrderList.right = rect.right - margins;
  118. m_OrderList.Init(rcOrderList, pMainFrm->GetGUIFont());
  119. // Toolbar buttons
  120. m_ToolBar.Init(CMainFrame::GetMainFrame()->m_PatternIcons, CMainFrame::GetMainFrame()->m_PatternIconsDisabled);
  121. m_ToolBar.AddButton(IDC_PATTERN_NEW, TIMAGE_PATTERN_NEW);
  122. m_ToolBar.AddButton(IDC_PATTERN_PLAY, TIMAGE_PATTERN_PLAY);
  123. m_ToolBar.AddButton(IDC_PATTERN_PLAYFROMSTART, TIMAGE_PATTERN_RESTART);
  124. m_ToolBar.AddButton(IDC_PATTERN_STOP, TIMAGE_PATTERN_STOP);
  125. m_ToolBar.AddButton(ID_PATTERN_PLAYROW, TIMAGE_PATTERN_PLAYROW);
  126. m_ToolBar.AddButton(IDC_PATTERN_RECORD, TIMAGE_PATTERN_RECORD, TBSTYLE_CHECK, (m_bRecord ? TBSTATE_CHECKED : 0) | TBSTATE_ENABLED);
  127. m_ToolBar.AddButton(ID_SEPARATOR, 0, TBSTYLE_SEP);
  128. m_ToolBar.AddButton(ID_PATTERN_VUMETERS, TIMAGE_PATTERN_VUMETERS, TBSTYLE_CHECK, (m_bVUMeters ? TBSTATE_CHECKED : 0) | TBSTATE_ENABLED);
  129. m_ToolBar.AddButton(ID_VIEWPLUGNAMES, TIMAGE_PATTERN_PLUGINS, TBSTYLE_CHECK, (m_bPluginNames ? TBSTATE_CHECKED : 0) | TBSTATE_ENABLED);
  130. m_ToolBar.AddButton(ID_PATTERN_CHANNELMANAGER, TIMAGE_CHANNELMANAGER);
  131. m_ToolBar.AddButton(ID_SEPARATOR, 0, TBSTYLE_SEP);
  132. m_ToolBar.AddButton(ID_PATTERN_MIDIMACRO, TIMAGE_MACROEDITOR);
  133. m_ToolBar.AddButton(ID_PATTERN_CHORDEDIT, TIMAGE_CHORDEDITOR);
  134. m_ToolBar.AddButton(ID_SEPARATOR, 0, TBSTYLE_SEP);
  135. m_ToolBar.AddButton(ID_EDIT_UNDO, TIMAGE_UNDO);
  136. m_ToolBar.AddButton(ID_PATTERN_PROPERTIES, TIMAGE_PATTERN_PROPERTIES);
  137. m_ToolBar.AddButton(ID_PATTERN_EXPAND, TIMAGE_PATTERN_EXPAND);
  138. m_ToolBar.AddButton(ID_PATTERN_SHRINK, TIMAGE_PATTERN_SHRINK);
  139. // m_ToolBar.AddButton(ID_PATTERN_AMPLIFY, TIMAGE_SAMPLE_AMPLIFY);
  140. m_ToolBar.AddButton(ID_SEPARATOR, 0, TBSTYLE_SEP);
  141. m_ToolBar.AddButton(ID_PATTERNDETAIL_LO, TIMAGE_PATTERN_DETAIL_LO, TBSTYLE_CHECK, TBSTATE_ENABLED);
  142. m_ToolBar.AddButton(ID_PATTERNDETAIL_MED, TIMAGE_PATTERN_DETAIL_MED, TBSTYLE_CHECK, TBSTATE_ENABLED);
  143. m_ToolBar.AddButton(ID_PATTERNDETAIL_HI, TIMAGE_PATTERN_DETAIL_HI, TBSTYLE_CHECK, TBSTATE_ENABLED | TBSTATE_CHECKED);
  144. m_ToolBar.AddButton(ID_SEPARATOR, 0, TBSTYLE_SEP);
  145. m_ToolBar.AddButton(ID_OVERFLOWPASTE, TIMAGE_PATTERN_OVERFLOWPASTE, TBSTYLE_CHECK, ((TrackerSettings::Instance().m_dwPatternSetup & PATTERN_OVERFLOWPASTE) ? TBSTATE_CHECKED : 0) | TBSTATE_ENABLED);
  146. // Special edit controls -> tab switch to view
  147. m_EditSequence.SetParent(this);
  148. m_EditSpacing.SetParent(this);
  149. m_EditPatName.SetParent(this);
  150. m_EditPatName.SetLimitText(MAX_PATTERNNAME - 1);
  151. // Spin controls
  152. m_SpinSpacing.SetRange32(0, MAX_SPACING);
  153. m_SpinSpacing.SetPos(TrackerSettings::Instance().gnPatternSpacing);
  154. m_SpinInstrument.SetRange32(-1, 1);
  155. m_SpinInstrument.SetPos(0);
  156. SetDlgItemInt(IDC_EDIT_SPACING, TrackerSettings::Instance().gnPatternSpacing);
  157. CheckDlgButton(IDC_PATTERN_FOLLOWSONG, !(TrackerSettings::Instance().m_dwPatternSetup & PATTERN_FOLLOWSONGOFF));
  158. m_SpinSequence.SetRange32(1, m_sndFile.Order.GetNumSequences());
  159. m_SpinSequence.SetPos(m_sndFile.Order.GetCurrentSequenceIndex() + 1);
  160. SetDlgItemText(IDC_EDIT_SEQUENCE_NAME, mpt::ToCString(Order().GetName()));
  161. m_OrderList.SetFocus();
  162. UpdateView(PatternHint().Names().ModType(), NULL);
  163. RecalcLayout();
  164. m_bInitialized = TRUE;
  165. UnlockControls();
  166. SetRedraw(TRUE);
  167. return FALSE;
  168. }
  169. void CCtrlPatterns::RecalcLayout()
  170. {
  171. // Update Order List Position
  172. if(m_OrderList.m_hWnd)
  173. {
  174. CRect rect;
  175. int cx, cy, cellcx;
  176. m_BtnNext.GetWindowRect(&rect);
  177. ScreenToClient(&rect);
  178. cx = -(rect.right + 4);
  179. cy = rect.bottom - rect.top + GetSystemMetrics(SM_CYHSCROLL);
  180. GetClientRect(&rect);
  181. cx += rect.right - 8;
  182. cellcx = m_OrderList.GetFontWidth();
  183. if(cellcx > 0)
  184. cx -= (cx % cellcx);
  185. cx += 2;
  186. if((cx > 0) && (cy > 0))
  187. {
  188. m_OrderList.SetWindowPos(NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER | SWP_DRAWFRAME);
  189. }
  190. }
  191. }
  192. void CCtrlPatterns::UpdateView(UpdateHint hint, CObject *pObj)
  193. {
  194. m_OrderList.UpdateView(hint, pObj);
  195. FlagSet<HintType> hintType = hint.GetType();
  196. const bool updateAll = hintType[HINT_MODTYPE];
  197. const bool updateSeq = hint.GetCategory() == HINTCAT_SEQUENCE;
  198. const bool updatePlug = hint.GetCategory() == HINTCAT_PLUGINS && hintType[HINT_MIXPLUGINS];
  199. const PatternHint patternHint = hint.ToType<PatternHint>();
  200. if(updateAll || (updateSeq && hintType[HINT_SEQNAMES]))
  201. {
  202. SetDlgItemText(IDC_EDIT_SEQUENCE_NAME, mpt::ToCString(Order().GetName()));
  203. }
  204. if(updateAll || (updateSeq && hintType[HINT_MODSEQUENCE]))
  205. {
  206. m_SpinSequence.SetRange(1, m_sndFile.Order.GetNumSequences());
  207. m_SpinSequence.SetPos(m_sndFile.Order.GetCurrentSequenceIndex() + 1);
  208. // Enable/disable multisequence controls according the current modtype.
  209. const BOOL isMultiSeqAvail = (m_sndFile.GetModSpecifications().sequencesMax > 1 || m_sndFile.Order.GetNumSequences() > 1) ? TRUE : FALSE;
  210. GetDlgItem(IDC_STATIC_SEQUENCE_NAME)->EnableWindow(isMultiSeqAvail);
  211. GetDlgItem(IDC_EDIT_SEQUENCE_NAME)->EnableWindow(isMultiSeqAvail);
  212. GetDlgItem(IDC_EDIT_SEQNUM)->EnableWindow(isMultiSeqAvail);
  213. GetDlgItem(IDC_SPIN_SEQNUM)->EnableWindow(isMultiSeqAvail);
  214. }
  215. if(updateAll || updatePlug)
  216. {
  217. GetDlgItem(IDC_PATINSTROPLUGGUI)->EnableWindow(HasValidPlug(m_nInstrument) ? TRUE : FALSE);
  218. }
  219. if(updateAll)
  220. {
  221. // Enable/disable pattern names
  222. const BOOL isPatNameAvail = m_sndFile.GetModSpecifications().hasPatternNames ? TRUE : FALSE;
  223. GetDlgItem(IDC_STATIC_PATTERNNAME)->EnableWindow(isPatNameAvail);
  224. GetDlgItem(IDC_EDIT_PATTERNNAME)->EnableWindow(isPatNameAvail);
  225. }
  226. if(hintType[HINT_MPTOPTIONS])
  227. {
  228. m_ToolBar.UpdateStyle();
  229. m_ToolBar.SetState(ID_OVERFLOWPASTE, ((TrackerSettings::Instance().m_dwPatternSetup & PATTERN_OVERFLOWPASTE) ? TBSTATE_CHECKED : 0) | TBSTATE_ENABLED);
  230. }
  231. bool instrPluginsChanged = false;
  232. if(hint.GetCategory() == HINTCAT_PLUGINS && hintType[HINT_PLUGINNAMES])
  233. {
  234. const auto changedPlug = hint.ToType<PluginHint>().GetPlugin();
  235. for(INSTRUMENTINDEX i = 1; i <= m_sndFile.GetNumInstruments(); i++)
  236. {
  237. const auto ins = m_sndFile.Instruments[i];
  238. if(ins != nullptr && (!changedPlug && ins->nMixPlug != 0) || (changedPlug && ins->nMixPlug == changedPlug))
  239. {
  240. instrPluginsChanged = true;
  241. break;
  242. }
  243. }
  244. }
  245. const bool updatePatNames = patternHint.GetType()[HINT_PATNAMES];
  246. const bool updateSmpNames = hint.GetCategory() == HINTCAT_SAMPLES && hintType[HINT_SMPNAMES];
  247. const bool updateInsNames = (hint.GetCategory() == HINTCAT_INSTRUMENTS && hintType[HINT_INSNAMES]) || instrPluginsChanged;
  248. if(updateAll || updatePatNames || updateSmpNames || updateInsNames)
  249. {
  250. LockControls();
  251. CString s;
  252. if(updateAll || updateSmpNames || updateInsNames)
  253. {
  254. constexpr TCHAR szSplitFormat[] = _T("%02u %s %02u: %s/%s");
  255. UINT nPos = 0;
  256. m_CbnInstrument.SetRedraw(FALSE);
  257. m_CbnInstrument.ResetContent();
  258. m_CbnInstrument.SetItemData(m_CbnInstrument.AddString(_T(" No Instrument")), 0);
  259. const INSTRUMENTINDEX nSplitIns = m_modDoc.GetSplitKeyboardSettings().splitInstrument;
  260. const ModCommand::NOTE noteSplit = 1 + m_modDoc.GetSplitKeyboardSettings().splitNote;
  261. const CString sSplitInsName = m_modDoc.GetPatternViewInstrumentName(nSplitIns, true, false);
  262. if(m_sndFile.GetNumInstruments())
  263. {
  264. // Show instrument names
  265. for(INSTRUMENTINDEX i = 1; i <= m_sndFile.GetNumInstruments(); i++)
  266. {
  267. if(m_sndFile.Instruments[i] == nullptr)
  268. continue;
  269. CString sDisplayName;
  270. if(m_modDoc.GetSplitKeyboardSettings().IsSplitActive())
  271. {
  272. s.Format(szSplitFormat,
  273. nSplitIns,
  274. mpt::ToCString(m_sndFile.GetNoteName(noteSplit, nSplitIns)).GetString(),
  275. i,
  276. sSplitInsName.GetString(),
  277. m_modDoc.GetPatternViewInstrumentName(i, true, false).GetString());
  278. sDisplayName = s;
  279. }
  280. else
  281. sDisplayName = m_modDoc.GetPatternViewInstrumentName(i);
  282. UINT n = m_CbnInstrument.AddString(sDisplayName);
  283. if(n == m_nInstrument) nPos = n;
  284. m_CbnInstrument.SetItemData(n, i);
  285. }
  286. } else
  287. {
  288. // Show sample names
  289. SAMPLEINDEX nmax = m_sndFile.GetNumSamples();
  290. for(SAMPLEINDEX i = 1; i <= nmax; i++) if (m_sndFile.GetSample(i).HasSampleData() || m_sndFile.GetSample(i).uFlags[CHN_ADLIB])
  291. {
  292. if (m_modDoc.GetSplitKeyboardSettings().IsSplitActive())
  293. s.Format(szSplitFormat,
  294. nSplitIns,
  295. mpt::ToCString(m_sndFile.GetNoteName(noteSplit, nSplitIns)).GetString(),
  296. i,
  297. mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.m_szNames[nSplitIns]).GetString(),
  298. mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.m_szNames[i]).GetString());
  299. else
  300. s.Format(_T("%02u: %s"),
  301. i,
  302. mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.m_szNames[i]).GetString());
  303. UINT n = m_CbnInstrument.AddString(s);
  304. if(n == m_nInstrument) nPos = n;
  305. m_CbnInstrument.SetItemData(n, i);
  306. }
  307. }
  308. m_CbnInstrument.SetCurSel(nPos);
  309. m_CbnInstrument.SetRedraw(TRUE);
  310. m_CbnInstrument.Invalidate(FALSE);
  311. }
  312. if(updateAll || updatePatNames)
  313. {
  314. PATTERNINDEX nPat;
  315. if(patternHint.GetType()[HINT_PATNAMES])
  316. nPat = patternHint.GetPattern();
  317. else
  318. nPat = (PATTERNINDEX)SendViewMessage(VIEWMSG_GETCURRENTPATTERN);
  319. if(m_sndFile.Patterns.IsValidIndex(nPat))
  320. {
  321. m_EditPatName.SetWindowText(mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.Patterns[nPat].GetName()));
  322. }
  323. BOOL bXMIT = (m_sndFile.GetType() & (MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT)) ? TRUE : FALSE;
  324. m_ToolBar.EnableButton(ID_PATTERN_MIDIMACRO, bXMIT);
  325. m_ToolBar.EnableButton(ID_PATTERN_PROPERTIES, bXMIT);
  326. m_ToolBar.EnableButton(ID_PATTERN_EXPAND, bXMIT);
  327. m_ToolBar.EnableButton(ID_PATTERN_SHRINK, bXMIT);
  328. }
  329. UnlockControls();
  330. }
  331. if(hintType[HINT_MODTYPE | HINT_UNDO])
  332. {
  333. m_ToolBar.EnableButton(ID_EDIT_UNDO, m_modDoc.GetPatternUndo().CanUndo());
  334. }
  335. }
  336. CRuntimeClass *CCtrlPatterns::GetAssociatedViewClass()
  337. {
  338. return RUNTIME_CLASS(CViewPattern);
  339. }
  340. LRESULT CCtrlPatterns::OnModCtrlMsg(WPARAM wParam, LPARAM lParam)
  341. {
  342. switch(wParam)
  343. {
  344. case CTRLMSG_GETCURRENTINSTRUMENT:
  345. return m_nInstrument;
  346. case CTRLMSG_GETCURRENTPATTERN:
  347. return m_OrderList.GetCurrentPattern();
  348. case CTRLMSG_PATTERNCHANGED:
  349. UpdateView(PatternHint(static_cast<PATTERNINDEX>(lParam)).Names());
  350. break;
  351. case CTRLMSG_PAT_PREVINSTRUMENT:
  352. OnPrevInstrument();
  353. break;
  354. case CTRLMSG_PAT_NEXTINSTRUMENT:
  355. OnNextInstrument();
  356. break;
  357. case CTRLMSG_NOTIFYCURRENTORDER:
  358. if(m_OrderList.GetCurSel().GetSelCount() > 1 || m_OrderList.m_bDragging)
  359. {
  360. // Only update play cursor in case there's a selection
  361. m_OrderList.Invalidate(FALSE);
  362. break;
  363. }
  364. // Otherwise, just act the same as a normal selection change
  365. [[fallthrough]];
  366. case CTRLMSG_SETCURRENTORDER:
  367. // Set order list selection and refresh GUI if change successful
  368. m_OrderList.SetCurSel(static_cast<ORDERINDEX>(lParam), false, false, true);
  369. break;
  370. case CTRLMSG_FORCEREFRESH:
  371. //refresh GUI
  372. m_OrderList.InvalidateRect(NULL, FALSE);
  373. break;
  374. case CTRLMSG_GETCURRENTORDER:
  375. return m_OrderList.GetCurSel(true).firstOrd;
  376. case CTRLMSG_SETCURRENTINSTRUMENT:
  377. case CTRLMSG_PAT_SETINSTRUMENT:
  378. return SetCurrentInstrument(static_cast<uint32>(lParam));
  379. case CTRLMSG_SETVIEWWND:
  380. {
  381. SendViewMessage(VIEWMSG_FOLLOWSONG, IsDlgButtonChecked(IDC_PATTERN_FOLLOWSONG));
  382. SendViewMessage(VIEWMSG_PATTERNLOOP, (m_sndFile.m_SongFlags & SONG_PATTERNLOOP) ? TRUE : FALSE);
  383. OnSpacingChanged();
  384. SendViewMessage(VIEWMSG_SETDETAIL, m_nDetailLevel);
  385. SendViewMessage(VIEWMSG_SETRECORD, m_bRecord);
  386. SendViewMessage(VIEWMSG_SETVUMETERS, m_bVUMeters);
  387. SendViewMessage(VIEWMSG_SETPLUGINNAMES, m_bPluginNames);
  388. }
  389. break;
  390. case CTRLMSG_SETSPACING:
  391. SetDlgItemInt(IDC_EDIT_SPACING, static_cast<UINT>(lParam));
  392. break;
  393. case CTRLMSG_SETFOCUS:
  394. GetParentFrame()->SetActiveView(&m_parent);
  395. m_OrderList.SetFocus();
  396. break;
  397. case CTRLMSG_SETRECORD:
  398. if (lParam >= 0) m_bRecord = (BOOL)(lParam); else m_bRecord = !m_bRecord;
  399. m_ToolBar.SetState(IDC_PATTERN_RECORD, ((m_bRecord) ? TBSTATE_CHECKED : 0)|TBSTATE_ENABLED);
  400. TrackerSettings::Instance().gbPatternRecord = (m_bRecord != 0);
  401. SendViewMessage(VIEWMSG_SETRECORD, m_bRecord);
  402. break;
  403. case CTRLMSG_PREVORDER:
  404. m_OrderList.SetCurSel(Order().GetPreviousOrderIgnoringSkips(m_OrderList.GetCurSel(true).firstOrd), true);
  405. break;
  406. case CTRLMSG_NEXTORDER:
  407. m_OrderList.SetCurSel(Order().GetNextOrderIgnoringSkips(m_OrderList.GetCurSel(true).firstOrd), true);
  408. break;
  409. //rewbs.customKeys
  410. case CTRLMSG_PAT_FOLLOWSONG:
  411. // parameters: 0 = turn off, 1 = toggle
  412. {
  413. UINT state = FALSE;
  414. if(lParam == 1) // toggle
  415. {
  416. state = !IsDlgButtonChecked(IDC_PATTERN_FOLLOWSONG);
  417. }
  418. CheckDlgButton(IDC_PATTERN_FOLLOWSONG, state);
  419. OnFollowSong();
  420. }
  421. break;
  422. case CTRLMSG_PAT_LOOP:
  423. {
  424. bool setLoop = false;
  425. if (lParam == -1)
  426. {
  427. //Toggle loop state
  428. setLoop = !m_sndFile.m_SongFlags[SONG_PATTERNLOOP];
  429. } else
  430. {
  431. setLoop = (lParam != 0);
  432. }
  433. m_sndFile.m_SongFlags.set(SONG_PATTERNLOOP, setLoop);
  434. CheckDlgButton(IDC_PATTERN_LOOP, setLoop ? BST_CHECKED : BST_UNCHECKED);
  435. break;
  436. }
  437. case CTRLMSG_PAT_NEWPATTERN:
  438. OnPatternNew();
  439. break;
  440. case CTRLMSG_PAT_DUPPATTERN:
  441. OnPatternDuplicate();
  442. break;
  443. case CTRLMSG_PAT_SETSEQUENCE:
  444. m_OrderList.SelectSequence(static_cast<SEQUENCEINDEX>(lParam));
  445. UpdateView(SequenceHint(static_cast<SEQUENCEINDEX>(lParam)).Names(), nullptr);
  446. break;
  447. default:
  448. return CModControlDlg::OnModCtrlMsg(wParam, lParam);
  449. }
  450. return 0;
  451. }
  452. void CCtrlPatterns::SetCurrentPattern(PATTERNINDEX nPat)
  453. {
  454. SendViewMessage(VIEWMSG_SETCURRENTPATTERN, (LPARAM)nPat);
  455. }
  456. BOOL CCtrlPatterns::SetCurrentInstrument(UINT nIns)
  457. {
  458. if(nIns == m_nInstrument)
  459. return TRUE;
  460. int n = m_CbnInstrument.GetCount();
  461. for(int i = 0; i < n; i++)
  462. {
  463. if(m_CbnInstrument.GetItemData(i) == nIns)
  464. {
  465. m_CbnInstrument.SetCurSel(i);
  466. m_nInstrument = static_cast<INSTRUMENTINDEX>(nIns);
  467. GetDlgItem(IDC_PATINSTROPLUGGUI)->EnableWindow(HasValidPlug(m_nInstrument) ? TRUE : FALSE);
  468. return TRUE;
  469. }
  470. }
  471. return FALSE;
  472. }
  473. ////////////////////////////////////////////////////////////
  474. // CCtrlPatterns messages
  475. void CCtrlPatterns::OnActivatePage(LPARAM lParam)
  476. {
  477. int nIns = m_parent.GetInstrumentChange();
  478. if(nIns > 0)
  479. {
  480. SetCurrentInstrument(nIns);
  481. }
  482. if(!(lParam & 0x80000000))
  483. {
  484. // Pattern item
  485. auto pat = static_cast<PATTERNINDEX>(lParam & 0xFFFF);
  486. if(m_sndFile.Patterns.IsValidIndex(pat))
  487. {
  488. for(SEQUENCEINDEX seq = 0; seq < m_sndFile.Order.GetNumSequences(); seq++)
  489. {
  490. if(ORDERINDEX ord = m_sndFile.Order(seq).FindOrder(pat); ord != ORDERINDEX_INVALID)
  491. {
  492. m_OrderList.SelectSequence(seq);
  493. m_OrderList.SetCurSel(ord, true);
  494. UpdateView(SequenceHint(seq).Names(), nullptr);
  495. break;
  496. }
  497. }
  498. }
  499. SetCurrentPattern(pat);
  500. } else if((lParam & 0x80000000))
  501. {
  502. // Order item
  503. auto ord = static_cast<ORDERINDEX>(lParam & 0xFFFF);
  504. auto seq = static_cast<SEQUENCEINDEX>((lParam >> 16) & 0x7FFF);
  505. if(seq < m_sndFile.Order.GetNumSequences())
  506. {
  507. m_OrderList.SelectSequence(seq);
  508. const auto &order = Order();
  509. if(ord < order.size())
  510. {
  511. m_OrderList.SetCurSel(ord);
  512. SetCurrentPattern(order[ord]);
  513. }
  514. UpdateView(SequenceHint(static_cast<SEQUENCEINDEX>(seq)).Names(), nullptr);
  515. }
  516. }
  517. if(m_hWndView)
  518. {
  519. OnSpacingChanged();
  520. if(m_bRecord)
  521. SendViewMessage(VIEWMSG_SETRECORD, m_bRecord);
  522. CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
  523. // Restore all save pattern state, except pattern number which we might have just set.
  524. PATTERNVIEWSTATE &patternViewState = pFrame->GetPatternViewState();
  525. if(patternViewState.initialOrder != ORDERINDEX_INVALID)
  526. {
  527. if(CMainFrame::GetMainFrame()->GetModPlaying() != &m_modDoc)
  528. m_OrderList.SetCurSel(patternViewState.initialOrder);
  529. patternViewState.initialOrder = ORDERINDEX_INVALID;
  530. }
  531. patternViewState.nPattern = static_cast<PATTERNINDEX>(SendViewMessage(VIEWMSG_GETCURRENTPATTERN));
  532. SendViewMessage(VIEWMSG_LOADSTATE, (LPARAM)&patternViewState);
  533. SwitchToView();
  534. }
  535. // Combo boxes randomly disappear without this... why?
  536. Invalidate();
  537. }
  538. void CCtrlPatterns::OnDeactivatePage()
  539. {
  540. CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
  541. if((pFrame) && (m_hWndView))
  542. SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&pFrame->GetPatternViewState());
  543. }
  544. void CCtrlPatterns::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
  545. {
  546. CModControlDlg::OnVScroll(nSBCode, nPos, pScrollBar);
  547. short int pos = (short int)m_SpinInstrument.GetPos();
  548. if(pos)
  549. {
  550. m_SpinInstrument.SetPos(0);
  551. if(pos < 0)
  552. OnPrevInstrument();
  553. else
  554. OnNextInstrument();
  555. }
  556. }
  557. void CCtrlPatterns::OnSequencePrev()
  558. {
  559. m_OrderList.SetCurSel(m_OrderList.GetCurSel(true).firstOrd - 1);
  560. m_OrderList.SetFocus();
  561. }
  562. void CCtrlPatterns::OnSequenceNext()
  563. {
  564. m_OrderList.SetCurSel(m_OrderList.GetCurSel(true).firstOrd + 1);
  565. m_OrderList.SetFocus();
  566. }
  567. void CCtrlPatterns::OnChannelManager()
  568. {
  569. m_modDoc.OnChannelManager();
  570. }
  571. void CCtrlPatterns::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  572. {
  573. CModControlDlg::OnKeyDown(nChar, nRepCnt, nFlags);
  574. }
  575. void CCtrlPatterns::OnSpacingChanged()
  576. {
  577. if((m_EditSpacing.m_hWnd) && (m_EditSpacing.GetWindowTextLength() > 0))
  578. {
  579. TrackerSettings::Instance().gnPatternSpacing = GetDlgItemInt(IDC_EDIT_SPACING);
  580. if(TrackerSettings::Instance().gnPatternSpacing > MAX_SPACING)
  581. {
  582. TrackerSettings::Instance().gnPatternSpacing = MAX_SPACING;
  583. SetDlgItemInt(IDC_EDIT_SPACING, TrackerSettings::Instance().gnPatternSpacing, FALSE);
  584. }
  585. SendViewMessage(VIEWMSG_SETSPACING, TrackerSettings::Instance().gnPatternSpacing);
  586. }
  587. }
  588. void CCtrlPatterns::OnInstrumentChanged()
  589. {
  590. int n = m_CbnInstrument.GetCurSel();
  591. if(n >= 0)
  592. {
  593. n = static_cast<int>(m_CbnInstrument.GetItemData(n));
  594. int nmax = (m_sndFile.m_nInstruments) ? m_sndFile.m_nInstruments : m_sndFile.m_nSamples;
  595. if((n >= 0) && (n <= nmax) && (n != (int)m_nInstrument))
  596. {
  597. m_nInstrument = static_cast<INSTRUMENTINDEX>(n);
  598. m_parent.InstrumentChanged(m_nInstrument);
  599. }
  600. SwitchToView();
  601. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_PATINSTROPLUGGUI), HasValidPlug(m_nInstrument));
  602. }
  603. }
  604. void CCtrlPatterns::OnPrevInstrument()
  605. {
  606. int n = m_CbnInstrument.GetCount();
  607. if(n > 0)
  608. {
  609. int pos = m_CbnInstrument.GetCurSel();
  610. if(pos > 0)
  611. pos--;
  612. else
  613. pos = n - 1;
  614. m_CbnInstrument.SetCurSel(pos);
  615. OnInstrumentChanged();
  616. }
  617. }
  618. void CCtrlPatterns::OnNextInstrument()
  619. {
  620. int n = m_CbnInstrument.GetCount();
  621. if(n > 0)
  622. {
  623. int pos = m_CbnInstrument.GetCurSel() + 1;
  624. if(pos >= n)
  625. pos = 0;
  626. m_CbnInstrument.SetCurSel(pos);
  627. OnInstrumentChanged();
  628. }
  629. }
  630. void CCtrlPatterns::OnPlayerPause()
  631. {
  632. CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
  633. if(pMainFrm)
  634. pMainFrm->PauseMod();
  635. }
  636. void CCtrlPatterns::OnPatternNew()
  637. {
  638. const auto &order = Order();
  639. ORDERINDEX curOrd = m_OrderList.GetCurSel(true).firstOrd;
  640. PATTERNINDEX curPat = (curOrd < order.size()) ? order[curOrd] : 0;
  641. ROWINDEX rows = 64;
  642. if(m_sndFile.Patterns.IsValidPat(curPat))
  643. {
  644. // Only if the current oder is already occupied, create a new pattern at the next position.
  645. curOrd++;
  646. } else
  647. {
  648. // Use currently edited pattern for new pattern length
  649. curPat = static_cast<PATTERNINDEX>(SendViewMessage(VIEWMSG_GETCURRENTPATTERN));
  650. }
  651. if(m_sndFile.Patterns.IsValidPat(curPat))
  652. {
  653. rows = m_sndFile.Patterns[curPat].GetNumRows();
  654. }
  655. rows = Clamp(rows, m_sndFile.GetModSpecifications().patternRowsMin, m_sndFile.GetModSpecifications().patternRowsMax);
  656. const PATTERNINDEX newPat = m_modDoc.InsertPattern(rows, curOrd);
  657. if(m_sndFile.Patterns.IsValidPat(newPat))
  658. {
  659. // update time signature
  660. if(m_sndFile.Patterns.IsValidIndex(curPat))
  661. {
  662. if(m_sndFile.Patterns[curPat].GetOverrideSignature())
  663. m_sndFile.Patterns[newPat].SetSignature(m_sndFile.Patterns[curPat].GetRowsPerBeat(), m_sndFile.Patterns[curPat].GetRowsPerMeasure());
  664. if(m_sndFile.Patterns[curPat].HasTempoSwing())
  665. m_sndFile.Patterns[newPat].SetTempoSwing(m_sndFile.Patterns[curPat].GetTempoSwing());
  666. }
  667. // move to new pattern
  668. m_OrderList.SetCurSel(curOrd);
  669. m_OrderList.Invalidate(FALSE);
  670. SetCurrentPattern(newPat);
  671. m_modDoc.SetModified();
  672. m_modDoc.UpdateAllViews(NULL, PatternHint(newPat).Names(), this);
  673. m_modDoc.UpdateAllViews(NULL, SequenceHint().Data(), this);
  674. SwitchToView();
  675. }
  676. }
  677. // Duplicates one or more patterns.
  678. void CCtrlPatterns::OnPatternDuplicate()
  679. {
  680. OrdSelection selection = m_OrderList.GetCurSel();
  681. const ORDERINDEX insertFrom = selection.firstOrd;
  682. const ORDERINDEX insertWhere = selection.lastOrd + 1u;
  683. if(insertWhere >= m_sndFile.GetModSpecifications().ordersMax)
  684. return;
  685. const ORDERINDEX insertCount = std::min(selection.GetSelCount(), static_cast<ORDERINDEX>(m_sndFile.GetModSpecifications().ordersMax - insertWhere));
  686. if(!insertCount)
  687. return;
  688. bool success = false, outOfPatterns = false;
  689. // Has this pattern been duplicated already? (for multiselect)
  690. std::vector<PATTERNINDEX> patReplaceIndex(m_sndFile.Patterns.Size(), PATTERNINDEX_INVALID);
  691. ModSequence &order = Order();
  692. for(ORDERINDEX i = 0; i < insertCount; i++)
  693. {
  694. PATTERNINDEX curPat = order[insertFrom + i];
  695. if(curPat < patReplaceIndex.size() && patReplaceIndex[curPat] == PATTERNINDEX_INVALID)
  696. {
  697. PATTERNINDEX newPat = m_sndFile.Patterns.Duplicate(curPat, true);
  698. if(newPat != PATTERNINDEX_INVALID)
  699. {
  700. order.insert(insertWhere + i, 1, newPat);
  701. success = true;
  702. // Mark as duplicated, so if this pattern is to be duplicated again, the same new pattern number is inserted into the order list.
  703. patReplaceIndex[curPat] = newPat;
  704. } else
  705. {
  706. if(m_sndFile.Patterns.IsValidPat(curPat))
  707. outOfPatterns = true;
  708. continue;
  709. }
  710. } else
  711. {
  712. // Invalid pattern, or it has been duplicated before (multiselect)
  713. PATTERNINDEX newPat;
  714. if(curPat < patReplaceIndex.size() && patReplaceIndex[curPat] != PATTERNINDEX_INVALID)
  715. {
  716. // Take care of patterns that have been duplicated before
  717. newPat = patReplaceIndex[curPat];
  718. } else
  719. {
  720. newPat = order[insertFrom + i];
  721. }
  722. order.insert(insertWhere + i, 1, newPat);
  723. success = true;
  724. }
  725. }
  726. if(success)
  727. {
  728. m_OrderList.InsertUpdatePlaystate(selection.firstOrd, selection.lastOrd);
  729. m_OrderList.Invalidate(FALSE);
  730. m_OrderList.SetCurSel(insertWhere, true, false, true);
  731. // 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.
  732. ORDERINDEX showPattern = std::min(insertWhere, order.GetLastIndex());
  733. while(!order.IsValidPat(showPattern) && showPattern < order.GetLastIndex())
  734. {
  735. showPattern++;
  736. }
  737. SetCurrentPattern(order[showPattern]);
  738. m_modDoc.SetModified();
  739. m_modDoc.UpdateAllViews(nullptr, SequenceHint().Data(), this);
  740. m_modDoc.UpdateAllViews(nullptr, PatternHint(PATTERNINDEX_INVALID).Names(), this);
  741. if(selection.lastOrd != selection.firstOrd)
  742. m_OrderList.m_nScrollPos2nd = insertWhere + insertCount - 1u;
  743. }
  744. if(outOfPatterns)
  745. {
  746. const auto &specs = m_sndFile.GetModSpecifications();
  747. Reporting::Error(MPT_AFORMAT("Pattern limit of the {} format ({} patterns) has been reached.")(mpt::ToUpperCaseAscii(specs.fileExtension), specs.patternsMax), "Duplicate Patterns");
  748. }
  749. SwitchToView();
  750. }
  751. // Merges one or more patterns into a single pattern
  752. void CCtrlPatterns::OnPatternMerge()
  753. {
  754. const OrdSelection selection = m_OrderList.GetCurSel();
  755. const ORDERINDEX firstOrder = selection.firstOrd;
  756. const ORDERINDEX numOrders = selection.GetSelCount();
  757. // Get the total number of lines to be merged
  758. ROWINDEX numRows = 0u;
  759. ModSequence &order = Order();
  760. for(ORDERINDEX i = 0; i < numOrders; i++)
  761. {
  762. PATTERNINDEX pat = order[firstOrder + i];
  763. if(m_sndFile.Patterns.IsValidPat(pat))
  764. numRows += m_sndFile.Patterns[pat].GetNumRows();
  765. }
  766. if(!numRows || numOrders < 2)
  767. {
  768. MessageBeep(MB_ICONWARNING);
  769. SwitchToView();
  770. return;
  771. }
  772. // Try to create a new pattern for the merge
  773. const auto &specs = m_sndFile.GetModSpecifications();
  774. const auto format = mpt::ToUpperCaseAscii(specs.fileExtension);
  775. if(numRows > specs.patternRowsMax)
  776. {
  777. Reporting::Error(MPT_AFORMAT("Merged pattern size ({} rows) exceeds the row limit ({} rows) of the {} format.")(numRows, specs.patternRowsMax, format), "Merge Patterns");
  778. SwitchToView();
  779. return;
  780. }
  781. CriticalSection cs;
  782. const PATTERNINDEX newPat = m_sndFile.Patterns.InsertAny(std::max(numRows, specs.patternRowsMin), true);
  783. if(newPat == PATTERNINDEX_INVALID)
  784. {
  785. cs.Leave();
  786. Reporting::Error(MPT_AFORMAT("Pattern limit of the {} format ({} patterns) has been reached.")(format, specs.patternsMax), "Merge Patterns");
  787. SwitchToView();
  788. return;
  789. }
  790. auto &pattern = m_sndFile.Patterns[newPat];
  791. auto it = pattern.begin();
  792. for(ORDERINDEX i = 0; i < numOrders; i++)
  793. {
  794. PATTERNINDEX pat = order[firstOrder + i];
  795. if(m_sndFile.Patterns.IsValidPat(pat))
  796. it = std::copy(m_sndFile.Patterns[pat].begin(), m_sndFile.Patterns[pat].end(), it);
  797. }
  798. if(pattern.GetNumRows() > numRows)
  799. pattern.WriteEffect(EffectWriter(CMD_PATTERNBREAK, 0).Row(numRows - 1).RetryNextRow());
  800. // Remove the merged patterns...
  801. order.Remove(selection.firstOrd, selection.lastOrd);
  802. m_OrderList.DeleteUpdatePlaystate(selection.firstOrd, selection.lastOrd);
  803. // ...and insert the new one
  804. order.insert(firstOrder, 1, newPat);
  805. m_OrderList.InsertUpdatePlaystate(firstOrder, firstOrder);
  806. m_OrderList.Invalidate(FALSE);
  807. m_OrderList.SetSelection(firstOrder);
  808. SetCurrentPattern(newPat);
  809. m_modDoc.SetModified();
  810. m_modDoc.UpdateAllViews(nullptr, SequenceHint().Data(), this);
  811. m_modDoc.UpdateAllViews(nullptr, PatternHint(PATTERNINDEX_INVALID).Names(), this);
  812. SwitchToView();
  813. }
  814. void CCtrlPatterns::OnPatternStop()
  815. {
  816. CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
  817. if(pMainFrm)
  818. pMainFrm->PauseMod(&m_modDoc);
  819. m_sndFile.ResetChannels();
  820. SwitchToView();
  821. }
  822. void CCtrlPatterns::OnPatternPlay()
  823. {
  824. m_modDoc.OnPatternPlay();
  825. SwitchToView();
  826. }
  827. //rewbs.playSongFromCursor
  828. void CCtrlPatterns::OnPatternPlayNoLoop()
  829. {
  830. m_modDoc.OnPatternPlayNoLoop();
  831. SwitchToView();
  832. }
  833. //end rewbs.playSongFromCursor
  834. void CCtrlPatterns::OnPatternPlayFromStart()
  835. {
  836. m_modDoc.OnPatternRestart();
  837. SwitchToView();
  838. }
  839. void CCtrlPatterns::OnPatternRecord()
  840. {
  841. UINT nState = m_ToolBar.GetState(IDC_PATTERN_RECORD);
  842. m_bRecord = ((nState & TBSTATE_CHECKED) != 0);
  843. TrackerSettings::Instance().gbPatternRecord = (m_bRecord != 0);
  844. SendViewMessage(VIEWMSG_SETRECORD, m_bRecord);
  845. SwitchToView();
  846. }
  847. void CCtrlPatterns::OnPatternVUMeters()
  848. {
  849. UINT nState = m_ToolBar.GetState(ID_PATTERN_VUMETERS);
  850. m_bVUMeters = ((nState & TBSTATE_CHECKED) != 0);
  851. TrackerSettings::Instance().gbPatternVUMeters = (m_bVUMeters != 0);
  852. SendViewMessage(VIEWMSG_SETVUMETERS, m_bVUMeters);
  853. SwitchToView();
  854. }
  855. //rewbs.patPlugName
  856. void CCtrlPatterns::OnPatternViewPlugNames()
  857. {
  858. UINT nState = m_ToolBar.GetState(ID_VIEWPLUGNAMES);
  859. m_bPluginNames = ((nState & TBSTATE_CHECKED) != 0);
  860. TrackerSettings::Instance().gbPatternPluginNames = (m_bPluginNames != 0);
  861. SendViewMessage(VIEWMSG_SETPLUGINNAMES, m_bPluginNames);
  862. SwitchToView();
  863. }
  864. //end rewbs.patPlugName
  865. void CCtrlPatterns::OnPatternProperties()
  866. {
  867. SendViewMessage(VIEWMSG_PATTERNPROPERTIES, PATTERNINDEX_INVALID);
  868. SwitchToView();
  869. }
  870. void CCtrlPatterns::OnPatternExpand()
  871. {
  872. SendViewMessage(VIEWMSG_EXPANDPATTERN);
  873. SwitchToView();
  874. }
  875. void CCtrlPatterns::OnPatternCopy()
  876. {
  877. SendViewMessage(VIEWMSG_COPYPATTERN);
  878. SwitchToView();
  879. }
  880. void CCtrlPatterns::OnPatternPaste()
  881. {
  882. SendViewMessage(VIEWMSG_PASTEPATTERN);
  883. SwitchToView();
  884. }
  885. void CCtrlPatterns::OnPatternShrink()
  886. {
  887. SendViewMessage(VIEWMSG_SHRINKPATTERN);
  888. SwitchToView();
  889. }
  890. void CCtrlPatterns::OnPatternAmplify()
  891. {
  892. SendViewMessage(VIEWMSG_AMPLIFYPATTERN);
  893. SwitchToView();
  894. }
  895. void CCtrlPatterns::OnPatternPlayRow()
  896. {
  897. ::SendMessage(m_hWndView, WM_COMMAND, ID_PATTERN_PLAYROW, 0);
  898. SwitchToView();
  899. }
  900. void CCtrlPatterns::OnUpdateRecord(CCmdUI *pCmdUI)
  901. {
  902. if(pCmdUI)
  903. pCmdUI->SetCheck((m_bRecord) ? TRUE : FALSE);
  904. }
  905. void CCtrlPatterns::OnFollowSong()
  906. {
  907. SendViewMessage(VIEWMSG_FOLLOWSONG, IsDlgButtonChecked(IDC_PATTERN_FOLLOWSONG));
  908. SwitchToView();
  909. }
  910. void CCtrlPatterns::OnChangeLoopStatus()
  911. {
  912. OnModCtrlMsg(CTRLMSG_PAT_LOOP, IsDlgButtonChecked(IDC_PATTERN_LOOP));
  913. SwitchToView();
  914. }
  915. void CCtrlPatterns::OnEditUndo()
  916. {
  917. if(m_hWndView)
  918. ::SendMessage(m_hWndView, WM_COMMAND, ID_EDIT_UNDO, 0);
  919. SwitchToView();
  920. }
  921. void CCtrlPatterns::OnSwitchToView()
  922. {
  923. PostViewMessage(VIEWMSG_SETFOCUS);
  924. }
  925. void CCtrlPatterns::OnPatternNameChanged()
  926. {
  927. if(!IsLocked())
  928. {
  929. const PATTERNINDEX nPat = (PATTERNINDEX)SendViewMessage(VIEWMSG_GETCURRENTPATTERN);
  930. CString tmp;
  931. m_EditPatName.GetWindowText(tmp);
  932. const std::string s = mpt::ToCharset(m_sndFile.GetCharsetInternal(), tmp);
  933. if(m_sndFile.Patterns[nPat].GetName() != s)
  934. {
  935. if(m_sndFile.Patterns[nPat].SetName(s))
  936. {
  937. if(m_sndFile.GetType() & (MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT))
  938. m_modDoc.SetModified();
  939. m_modDoc.UpdateAllViews(NULL, PatternHint(nPat).Names(), this);
  940. }
  941. }
  942. }
  943. }
  944. void CCtrlPatterns::OnSequenceNameChanged()
  945. {
  946. CString tmp;
  947. GetDlgItemText(IDC_EDIT_SEQUENCE_NAME, tmp);
  948. const mpt::ustring str = mpt::ToUnicode(tmp);
  949. auto &order = Order();
  950. if(str != order.GetName())
  951. {
  952. order.SetName(str);
  953. m_modDoc.SetModified();
  954. m_modDoc.UpdateAllViews(nullptr, SequenceHint(m_sndFile.Order.GetCurrentSequenceIndex()).Names(), this);
  955. }
  956. }
  957. void CCtrlPatterns::OnChordEditor()
  958. {
  959. CChordEditor dlg(this);
  960. dlg.DoModal();
  961. SwitchToView();
  962. }
  963. void CCtrlPatterns::OnDetailLo()
  964. {
  965. m_ToolBar.SetState(ID_PATTERNDETAIL_LO, TBSTATE_CHECKED | TBSTATE_ENABLED);
  966. if(m_nDetailLevel != PatternCursor::instrColumn)
  967. {
  968. m_nDetailLevel = PatternCursor::instrColumn;
  969. m_ToolBar.SetState(ID_PATTERNDETAIL_MED, TBSTATE_ENABLED);
  970. m_ToolBar.SetState(ID_PATTERNDETAIL_HI, TBSTATE_ENABLED);
  971. SendViewMessage(VIEWMSG_SETDETAIL, m_nDetailLevel);
  972. }
  973. SwitchToView();
  974. }
  975. void CCtrlPatterns::OnDetailMed()
  976. {
  977. m_ToolBar.SetState(ID_PATTERNDETAIL_MED, TBSTATE_CHECKED | TBSTATE_ENABLED);
  978. if(m_nDetailLevel != PatternCursor::volumeColumn)
  979. {
  980. m_nDetailLevel = PatternCursor::volumeColumn;
  981. m_ToolBar.SetState(ID_PATTERNDETAIL_LO, TBSTATE_ENABLED);
  982. m_ToolBar.SetState(ID_PATTERNDETAIL_HI, TBSTATE_ENABLED);
  983. SendViewMessage(VIEWMSG_SETDETAIL, m_nDetailLevel);
  984. }
  985. SwitchToView();
  986. }
  987. void CCtrlPatterns::OnDetailHi()
  988. {
  989. m_ToolBar.SetState(ID_PATTERNDETAIL_HI, TBSTATE_CHECKED | TBSTATE_ENABLED);
  990. if(m_nDetailLevel != PatternCursor::lastColumn)
  991. {
  992. m_nDetailLevel = PatternCursor::lastColumn;
  993. m_ToolBar.SetState(ID_PATTERNDETAIL_LO, TBSTATE_ENABLED);
  994. m_ToolBar.SetState(ID_PATTERNDETAIL_MED, TBSTATE_ENABLED);
  995. SendViewMessage(VIEWMSG_SETDETAIL, m_nDetailLevel);
  996. }
  997. SwitchToView();
  998. }
  999. void CCtrlPatterns::OnToggleOverflowPaste()
  1000. {
  1001. TrackerSettings::Instance().m_dwPatternSetup ^= PATTERN_OVERFLOWPASTE;
  1002. UpdateView(UpdateHint().MPTOptions());
  1003. SwitchToView();
  1004. }
  1005. void CCtrlPatterns::TogglePluginEditor()
  1006. {
  1007. if(m_sndFile.GetInstrumentPlugin(m_nInstrument) != nullptr)
  1008. {
  1009. m_modDoc.TogglePluginEditor(m_sndFile.Instruments[m_nInstrument]->nMixPlug - 1, CMainFrame::GetInputHandler()->ShiftPressed());
  1010. }
  1011. }
  1012. bool CCtrlPatterns::HasValidPlug(INSTRUMENTINDEX instr) const
  1013. {
  1014. return m_sndFile.GetInstrumentPlugin(instr) != nullptr;
  1015. }
  1016. BOOL CCtrlPatterns::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
  1017. {
  1018. if(nFlags == 0)
  1019. {
  1020. PostViewMessage(VIEWMSG_DOSCROLL, zDelta);
  1021. }
  1022. return CModControlDlg::OnMouseWheel(nFlags, zDelta, pt);
  1023. }
  1024. void CCtrlPatterns::OnXButtonUp(UINT nFlags, UINT nButton, CPoint point)
  1025. {
  1026. if(nButton == XBUTTON1)
  1027. OnModCtrlMsg(CTRLMSG_PREVORDER, 0);
  1028. else if(nButton == XBUTTON2)
  1029. OnModCtrlMsg(CTRLMSG_NEXTORDER, 0);
  1030. CModControlDlg::OnXButtonUp(nFlags, nButton, point);
  1031. }
  1032. BOOL CCtrlPatterns::OnToolTip(UINT /*id*/, NMHDR *pNMHDR, LRESULT * /*pResult*/)
  1033. {
  1034. TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
  1035. UINT_PTR nID = pNMHDR->idFrom;
  1036. if(pTTT->uFlags & TTF_IDISHWND)
  1037. {
  1038. // idFrom is actually the HWND of the tool
  1039. nID = ::GetDlgCtrlID((HWND)nID);
  1040. if(nID)
  1041. {
  1042. pTTT->lpszText = MAKEINTRESOURCE(nID);
  1043. pTTT->hinst = AfxGetResourceHandle();
  1044. return TRUE;
  1045. }
  1046. }
  1047. return FALSE;
  1048. }
  1049. BOOL CCtrlPatterns::GetToolTipText(UINT id, LPTSTR str)
  1050. {
  1051. CString fmt;
  1052. const TCHAR *s = nullptr;
  1053. CommandID cmd = kcNull;
  1054. switch(id)
  1055. {
  1056. case IDC_PATTERN_NEW: s = _T("Insert Pattern"); cmd = kcNewPattern; break;
  1057. case IDC_PATTERN_PLAY: s = _T("Play Pattern"); cmd = kcPlayPatternFromCursor; break;
  1058. case IDC_PATTERN_PLAYFROMSTART: s = _T("Replay Pattern"); cmd = kcPlayPatternFromStart; break;
  1059. case IDC_PATTERN_STOP: s = _T("Stop"); cmd = kcPauseSong; break;
  1060. case ID_PATTERN_PLAYROW: s = _T("Play Row"); cmd = kcPatternPlayRow; break;
  1061. case IDC_PATTERN_RECORD: s = _T("Record"); cmd = kcPatternRecord; break;
  1062. case ID_PATTERN_VUMETERS: s = _T("VU-Meters"); break;
  1063. case ID_VIEWPLUGNAMES: s = _T("Show Plugins"); break;
  1064. case ID_PATTERN_CHANNELMANAGER: s = _T("Channel Manager"); cmd = kcViewChannelManager; break;
  1065. case ID_PATTERN_MIDIMACRO: s = _T("Zxx Macro Configuration"); cmd = kcShowMacroConfig; break;
  1066. case ID_PATTERN_CHORDEDIT: s = _T("Chord Editor"); cmd = kcChordEditor; break;
  1067. case ID_EDIT_UNDO:
  1068. fmt = _T("Undo");
  1069. if(m_modDoc.GetPatternUndo().CanUndo())
  1070. fmt += _T(" ") + m_modDoc.GetPatternUndo().GetUndoName();
  1071. cmd = kcEditUndo;
  1072. break;
  1073. case ID_PATTERN_PROPERTIES: s = _T("Pattern Properties"); cmd = kcShowPatternProperties; break;
  1074. case ID_PATTERN_EXPAND: s = _T("Expand Pattern"); break;
  1075. case ID_PATTERN_SHRINK: s = _T("Shrink Pattern"); break;
  1076. case ID_PATTERNDETAIL_LO: s = _T("Low Pattern Detail Level"); break;
  1077. case ID_PATTERNDETAIL_MED: s = _T("Medium Pattern Detail Level"); break;
  1078. case ID_PATTERNDETAIL_HI: s = _T("High Pattern Detail Level"); break;
  1079. case ID_OVERFLOWPASTE: s = _T("Toggle Overflow Paste"); cmd = kcToggleOverflowPaste; break;
  1080. case IDC_PATTERN_LOOP: s = _T("Toggle Loop Pattern"); cmd = kcChangeLoopStatus; break;
  1081. case IDC_PATTERN_FOLLOWSONG: s = _T("Toggle Follow Song"); cmd = kcToggleFollowSong; break;
  1082. default:
  1083. return FALSE;
  1084. }
  1085. if(s != nullptr)
  1086. fmt = s;
  1087. if(cmd != kcNull)
  1088. {
  1089. auto keyText = CMainFrame::GetInputHandler()->m_activeCommandSet->GetKeyTextFromCommand(cmd, 0);
  1090. if(!keyText.IsEmpty())
  1091. fmt += MPT_CFORMAT(" ({})")(keyText);
  1092. }
  1093. _tcscpy(str, fmt.GetString());
  1094. return TRUE;
  1095. }
  1096. void CCtrlPatterns::OnSequenceNumChanged()
  1097. {
  1098. if((m_EditSequence.m_hWnd) && (m_EditSequence.GetWindowTextLength() > 0))
  1099. {
  1100. SEQUENCEINDEX newSeq = static_cast<SEQUENCEINDEX>(GetDlgItemInt(IDC_EDIT_SEQNUM) - 1);
  1101. if(newSeq == m_sndFile.Order.GetCurrentSequenceIndex())
  1102. return;
  1103. if(newSeq >= m_sndFile.Order.GetNumSequences())
  1104. {
  1105. newSeq = m_sndFile.Order.GetNumSequences() - 1;
  1106. SetDlgItemInt(IDC_EDIT_SEQNUM, newSeq + 1, FALSE);
  1107. }
  1108. m_OrderList.SelectSequence(newSeq);
  1109. UpdateView(SequenceHint(newSeq).Names(), nullptr);
  1110. }
  1111. }
  1112. OPENMPT_NAMESPACE_END