PatternEditorDialogs.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695
  1. /*
  2. * PatternEditorDialogs.cpp
  3. * ------------------------
  4. * Purpose: Code for various dialogs that are used in the pattern editor.
  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 "Moddoc.h"
  15. #include "View_pat.h"
  16. #include "PatternEditorDialogs.h"
  17. #include "TempoSwingDialog.h"
  18. #include "../soundlib/mod_specifications.h"
  19. #include "../common/mptStringBuffer.h"
  20. OPENMPT_NAMESPACE_BEGIN
  21. static constexpr EffectCommand ExtendedCommands[] = {CMD_OFFSET, CMD_PATTERNBREAK, CMD_POSITIONJUMP, CMD_TEMPO, CMD_FINETUNE, CMD_FINETUNE_SMOOTH};
  22. // For a given pattern cell, check if it contains a command supported by the X-Param mechanism.
  23. // If so, calculate the multipler for this cell and the value of all the other cells belonging to this param.
  24. void getXParam(ModCommand::COMMAND command, PATTERNINDEX nPat, ROWINDEX nRow, CHANNELINDEX nChannel, const CSoundFile &sndFile, UINT &xparam, UINT &multiplier)
  25. {
  26. UINT xp = 0, mult = 1;
  27. int cmdRow = static_cast<int>(nRow);
  28. const auto &pattern = sndFile.Patterns[nPat];
  29. if(command == CMD_XPARAM)
  30. {
  31. // current command is a parameter extension command
  32. cmdRow--;
  33. // Try to find previous command parameter to be extended
  34. while(cmdRow >= 0)
  35. {
  36. const ModCommand &m = *pattern.GetpModCommand(cmdRow, nChannel);
  37. if(mpt::contains(ExtendedCommands, m.command))
  38. break;
  39. if(m.command != CMD_XPARAM)
  40. {
  41. cmdRow = -1;
  42. break;
  43. }
  44. cmdRow--;
  45. }
  46. } else if(!mpt::contains(ExtendedCommands, command))
  47. {
  48. // If current row do not own any satisfying command parameter to extend, set return state
  49. cmdRow = -1;
  50. }
  51. if(cmdRow >= 0)
  52. {
  53. // An 'extendable' command parameter has been found
  54. const ModCommand &m = *pattern.GetpModCommand(cmdRow, nChannel);
  55. // Find extension resolution (8 to 24 bits)
  56. uint32 n = 1;
  57. while(n < 4 && cmdRow + n < pattern.GetNumRows())
  58. {
  59. if(pattern.GetpModCommand(cmdRow + n, nChannel)->command != CMD_XPARAM)
  60. break;
  61. n++;
  62. }
  63. // Parameter extension found (above 8 bits non-standard parameters)
  64. if(n > 1)
  65. {
  66. // Limit offset command to 24 bits, other commands to 16 bits
  67. n = m.command == CMD_OFFSET ? n : (n > 2 ? 2 : n);
  68. // Compute extended value WITHOUT current row parameter value : this parameter
  69. // is being currently edited (this is why this function is being called) so we
  70. // only need to compute a multiplier so that we can add its contribution while
  71. // its value is changed by user
  72. for(uint32 j = 0; j < n; j++)
  73. {
  74. const ModCommand &mx = *pattern.GetpModCommand(cmdRow + j, nChannel);
  75. uint32 k = 8 * (n - j - 1);
  76. if(cmdRow + j == nRow)
  77. mult = 1 << k;
  78. else
  79. xp += (mx.param << k);
  80. }
  81. } else if(m.command == CMD_OFFSET || m.command == CMD_FINETUNE || m.command == CMD_FINETUNE_SMOOTH)
  82. {
  83. // No parameter extension to perform (8 bits standard parameter),
  84. // just care about offset command special case (16 bits, fake)
  85. mult <<= 8;
  86. }
  87. const auto modDoc = sndFile.GetpModDoc();
  88. if(m.command == CMD_OFFSET && m.volcmd == VOLCMD_OFFSET && modDoc != nullptr)
  89. {
  90. SAMPLEINDEX smp = modDoc->GetSampleIndex(m);
  91. if(m.vol == 0 && smp != 0)
  92. {
  93. xp = Util::muldivr_unsigned(sndFile.GetSample(smp).nLength, pattern.GetpModCommand(nRow, nChannel)->param * mult + xp, 256u << (8u * (std::max(uint32(2), n) - 1u)));
  94. mult = 0;
  95. } else if(m.vol > 0 && smp != 0)
  96. {
  97. xp += sndFile.GetSample(smp).cues[m.vol - 1];
  98. }
  99. }
  100. }
  101. // Return x-parameter
  102. multiplier = mult;
  103. xparam = xp;
  104. }
  105. /////////////////////////////////////////////////////////////////////////////////////////////
  106. // CPatternPropertiesDlg
  107. BEGIN_MESSAGE_MAP(CPatternPropertiesDlg, CDialog)
  108. ON_COMMAND(IDC_BUTTON_HALF, &CPatternPropertiesDlg::OnHalfRowNumber)
  109. ON_COMMAND(IDC_BUTTON_DOUBLE, &CPatternPropertiesDlg::OnDoubleRowNumber)
  110. ON_COMMAND(IDC_CHECK1, &CPatternPropertiesDlg::OnOverrideSignature)
  111. ON_COMMAND(IDC_BUTTON1, &CPatternPropertiesDlg::OnTempoSwing)
  112. END_MESSAGE_MAP()
  113. BOOL CPatternPropertiesDlg::OnInitDialog()
  114. {
  115. CComboBox *combo;
  116. CDialog::OnInitDialog();
  117. combo = (CComboBox *)GetDlgItem(IDC_COMBO1);
  118. const CSoundFile &sndFile = modDoc.GetSoundFile();
  119. if(m_nPattern < sndFile.Patterns.Size() && combo)
  120. {
  121. CString s;
  122. const CPattern &pattern = sndFile.Patterns[m_nPattern];
  123. ROWINDEX nrows = pattern.GetNumRows();
  124. const CModSpecifications &specs = sndFile.GetModSpecifications();
  125. combo->SetRedraw(FALSE);
  126. for(UINT irow = specs.patternRowsMin; irow <= specs.patternRowsMax; irow++)
  127. {
  128. combo->AddString(mpt::cfmt::dec(irow));
  129. }
  130. combo->SetCurSel(nrows - specs.patternRowsMin);
  131. combo->SetRedraw(TRUE);
  132. CheckRadioButton(IDC_RADIO1, IDC_RADIO2, IDC_RADIO2);
  133. s = MPT_CFORMAT("Pattern #{}: {} row{} ({}K)")(
  134. m_nPattern,
  135. pattern.GetNumRows(),
  136. (pattern.GetNumRows() == 1) ? CString(_T("")) : CString(_T("s")),
  137. static_cast<int>((pattern.GetNumRows() * sndFile.GetNumChannels() * sizeof(ModCommand)) / 1024));
  138. SetDlgItemText(IDC_TEXT1, s);
  139. // Window title
  140. const CString patternName = mpt::ToCString(sndFile.GetCharsetInternal(), pattern.GetName());
  141. s = MPT_CFORMAT("Pattern Properties for Pattern #{}")(m_nPattern);
  142. if(!patternName.IsEmpty())
  143. {
  144. s += _T(" (");
  145. s += patternName;
  146. s += _T(")");
  147. }
  148. SetWindowText(s);
  149. // Pattern time signature
  150. const bool bOverride = pattern.GetOverrideSignature();
  151. ROWINDEX nRPB = pattern.GetRowsPerBeat(), nRPM = pattern.GetRowsPerMeasure();
  152. if(nRPB == 0 || !bOverride)
  153. nRPB = sndFile.m_nDefaultRowsPerBeat;
  154. if(nRPM == 0 || !bOverride)
  155. nRPM = sndFile.m_nDefaultRowsPerMeasure;
  156. m_tempoSwing = pattern.HasTempoSwing() ? pattern.GetTempoSwing() : sndFile.m_tempoSwing;
  157. GetDlgItem(IDC_CHECK1)->EnableWindow(sndFile.GetModSpecifications().hasPatternSignatures ? TRUE : FALSE);
  158. CheckDlgButton(IDC_CHECK1, bOverride ? BST_CHECKED : BST_UNCHECKED);
  159. SetDlgItemInt(IDC_ROWSPERBEAT, nRPB, FALSE);
  160. SetDlgItemInt(IDC_ROWSPERMEASURE, nRPM, FALSE);
  161. OnOverrideSignature();
  162. }
  163. return TRUE;
  164. }
  165. void CPatternPropertiesDlg::OnHalfRowNumber()
  166. {
  167. const CSoundFile &sndFile = modDoc.GetSoundFile();
  168. UINT nRows = GetDlgItemInt(IDC_COMBO1, NULL, FALSE);
  169. nRows /= 2;
  170. if(nRows < sndFile.GetModSpecifications().patternRowsMin)
  171. nRows = sndFile.GetModSpecifications().patternRowsMin;
  172. SetDlgItemInt(IDC_COMBO1, nRows, FALSE);
  173. }
  174. void CPatternPropertiesDlg::OnDoubleRowNumber()
  175. {
  176. const CSoundFile &sndFile = modDoc.GetSoundFile();
  177. UINT nRows = GetDlgItemInt(IDC_COMBO1, NULL, FALSE);
  178. nRows *= 2;
  179. if(nRows > sndFile.GetModSpecifications().patternRowsMax)
  180. nRows = sndFile.GetModSpecifications().patternRowsMax;
  181. SetDlgItemInt(IDC_COMBO1, nRows, FALSE);
  182. }
  183. void CPatternPropertiesDlg::OnOverrideSignature()
  184. {
  185. GetDlgItem(IDC_ROWSPERBEAT)->EnableWindow(IsDlgButtonChecked(IDC_CHECK1));
  186. GetDlgItem(IDC_ROWSPERMEASURE)->EnableWindow(IsDlgButtonChecked(IDC_CHECK1));
  187. GetDlgItem(IDC_BUTTON1)->EnableWindow(IsDlgButtonChecked(IDC_CHECK1) && modDoc.GetSoundFile().m_nTempoMode == TempoMode::Modern);
  188. }
  189. void CPatternPropertiesDlg::OnTempoSwing()
  190. {
  191. CPattern &pat = modDoc.GetSoundFile().Patterns[m_nPattern];
  192. const ROWINDEX oldRPB = pat.GetRowsPerBeat();
  193. const ROWINDEX oldRPM = pat.GetRowsPerMeasure();
  194. // Temporarily apply new tempo signature for preview
  195. const ROWINDEX newRPB = std::clamp(static_cast<ROWINDEX>(GetDlgItemInt(IDC_ROWSPERBEAT)), ROWINDEX(1), MAX_ROWS_PER_BEAT);
  196. const ROWINDEX newRPM = std::clamp(static_cast<ROWINDEX>(GetDlgItemInt(IDC_ROWSPERMEASURE)), newRPB, MAX_ROWS_PER_BEAT);
  197. pat.SetSignature(newRPB, newRPM);
  198. m_tempoSwing.resize(newRPB, TempoSwing::Unity);
  199. CTempoSwingDlg dlg(this, m_tempoSwing, modDoc.GetSoundFile(), m_nPattern);
  200. if(dlg.DoModal() == IDOK)
  201. {
  202. m_tempoSwing = dlg.m_tempoSwing;
  203. }
  204. pat.SetSignature(oldRPB, oldRPM);
  205. }
  206. void CPatternPropertiesDlg::OnOK()
  207. {
  208. CSoundFile &sndFile = modDoc.GetSoundFile();
  209. CPattern &pattern = sndFile.Patterns[m_nPattern];
  210. // Update pattern signature if necessary
  211. if(sndFile.GetModSpecifications().hasPatternSignatures)
  212. {
  213. if(IsDlgButtonChecked(IDC_CHECK1))
  214. {
  215. // Enable signature
  216. const ROWINDEX newRPB = std::min(static_cast<ROWINDEX>(GetDlgItemInt(IDC_ROWSPERBEAT, NULL, FALSE)), MAX_ROWS_PER_BEAT);
  217. const ROWINDEX newRPM = std::min(static_cast<ROWINDEX>(GetDlgItemInt(IDC_ROWSPERMEASURE, NULL, FALSE)), MAX_ROWS_PER_BEAT);
  218. if(newRPB != pattern.GetRowsPerBeat() || newRPM != pattern.GetRowsPerMeasure() || m_tempoSwing != pattern.GetTempoSwing())
  219. {
  220. if(!pattern.SetSignature(newRPB, newRPM))
  221. {
  222. Reporting::Error("Invalid time signature!", "Pattern Properties");
  223. GetDlgItem(IDC_ROWSPERBEAT)->SetFocus();
  224. return;
  225. }
  226. m_tempoSwing.resize(newRPB, TempoSwing::Unity);
  227. pattern.SetTempoSwing(m_tempoSwing);
  228. modDoc.SetModified();
  229. }
  230. } else
  231. {
  232. // Disable signature
  233. if(pattern.GetOverrideSignature() || pattern.HasTempoSwing())
  234. {
  235. pattern.RemoveSignature();
  236. pattern.RemoveTempoSwing();
  237. modDoc.SetModified();
  238. }
  239. }
  240. }
  241. const ROWINDEX newSize = (ROWINDEX)GetDlgItemInt(IDC_COMBO1, NULL, FALSE);
  242. // Check if any pattern data would be removed.
  243. bool resize = (newSize != sndFile.Patterns[m_nPattern].GetNumRows());
  244. bool resizeAtEnd = IsDlgButtonChecked(IDC_RADIO2) != BST_UNCHECKED;
  245. if(newSize < sndFile.Patterns[m_nPattern].GetNumRows())
  246. {
  247. ROWINDEX firstRow = resizeAtEnd ? newSize : 0;
  248. ROWINDEX lastRow = resizeAtEnd ? sndFile.Patterns[m_nPattern].GetNumRows() : sndFile.Patterns[m_nPattern].GetNumRows() - newSize;
  249. for(ROWINDEX row = firstRow; row < lastRow; row++)
  250. {
  251. if(!sndFile.Patterns[m_nPattern].IsEmptyRow(row))
  252. {
  253. resize = (Reporting::Confirm(MPT_AFORMAT("Data at the {} of the pattern will be lost.\nDo you want to continue?")(resizeAtEnd ? "end" : "start"), "Shrink Pattern") == cnfYes);
  254. break;
  255. }
  256. }
  257. }
  258. if(resize)
  259. {
  260. modDoc.BeginWaitCursor();
  261. modDoc.GetPatternUndo().PrepareUndo(m_nPattern, 0, 0, sndFile.Patterns[m_nPattern].GetNumChannels(), sndFile.Patterns[m_nPattern].GetNumRows(), "Resize");
  262. if(sndFile.Patterns[m_nPattern].Resize(newSize, true, resizeAtEnd))
  263. {
  264. modDoc.SetModified();
  265. }
  266. modDoc.EndWaitCursor();
  267. }
  268. CDialog::OnOK();
  269. }
  270. ////////////////////////////////////////////////////////////////////////////////////////////
  271. // CEditCommand
  272. BEGIN_MESSAGE_MAP(CEditCommand, CDialog)
  273. ON_WM_ACTIVATE()
  274. ON_WM_CLOSE()
  275. ON_CBN_SELCHANGE(IDC_COMBO1, &CEditCommand::OnNoteChanged)
  276. ON_CBN_SELCHANGE(IDC_COMBO2, &CEditCommand::OnNoteChanged)
  277. ON_CBN_SELCHANGE(IDC_COMBO3, &CEditCommand::OnVolCmdChanged)
  278. ON_CBN_SELCHANGE(IDC_COMBO4, &CEditCommand::OnCommandChanged)
  279. ON_CBN_SELCHANGE(IDC_COMBO5, &CEditCommand::OnPlugParamChanged)
  280. ON_WM_HSCROLL()
  281. END_MESSAGE_MAP()
  282. void CEditCommand::DoDataExchange(CDataExchange* pDX)
  283. {
  284. CDialog::DoDataExchange(pDX);
  285. //{{AFX_DATA_MAP(CSplitKeyboadSettings)
  286. DDX_Control(pDX, IDC_COMBO1, cbnNote);
  287. DDX_Control(pDX, IDC_COMBO2, cbnInstr);
  288. DDX_Control(pDX, IDC_COMBO3, cbnVolCmd);
  289. DDX_Control(pDX, IDC_COMBO4, cbnCommand);
  290. DDX_Control(pDX, IDC_COMBO5, cbnPlugParam);
  291. DDX_Control(pDX, IDC_SLIDER1, sldVolParam);
  292. DDX_Control(pDX, IDC_SLIDER2, sldParam);
  293. //}}AFX_DATA_MAP
  294. }
  295. CEditCommand::CEditCommand(CSoundFile &sndFile)
  296. : sndFile(sndFile), effectInfo(sndFile)
  297. {
  298. CDialog::Create(IDD_PATTERN_EDITCOMMAND);
  299. }
  300. BOOL CEditCommand::PreTranslateMessage(MSG *pMsg)
  301. {
  302. if((pMsg) && (pMsg->message == WM_KEYDOWN))
  303. {
  304. if((pMsg->wParam == VK_ESCAPE) || (pMsg->wParam == VK_RETURN) || (pMsg->wParam == VK_APPS))
  305. {
  306. OnClose();
  307. return TRUE;
  308. }
  309. }
  310. return CDialog::PreTranslateMessage(pMsg);
  311. }
  312. bool CEditCommand::ShowEditWindow(PATTERNINDEX pat, const PatternCursor &cursor, CWnd *parent)
  313. {
  314. editPattern = pat;
  315. const ROWINDEX row = editRow = cursor.GetRow();
  316. const CHANNELINDEX chn = editChannel = cursor.GetChannel();
  317. if(!sndFile.Patterns.IsValidPat(pat)
  318. || !sndFile.Patterns[pat].IsValidRow(row)
  319. || chn >= sndFile.GetNumChannels())
  320. {
  321. ShowWindow(SW_HIDE);
  322. return false;
  323. }
  324. m = sndFile.Patterns[pat].GetpModCommand(row, chn);
  325. modified = false;
  326. InitAll();
  327. switch(cursor.GetColumnType())
  328. {
  329. case PatternCursor::noteColumn:
  330. cbnNote.SetFocus();
  331. break;
  332. case PatternCursor::instrColumn:
  333. cbnInstr.SetFocus();
  334. break;
  335. case PatternCursor::volumeColumn:
  336. if(m->IsPcNote())
  337. cbnPlugParam.SetFocus();
  338. else
  339. cbnVolCmd.SetFocus();
  340. break;
  341. case PatternCursor::effectColumn:
  342. if(m->IsPcNote())
  343. sldParam.SetFocus();
  344. else
  345. cbnCommand.SetFocus();
  346. break;
  347. case PatternCursor::paramColumn:
  348. sldParam.SetFocus();
  349. break;
  350. }
  351. // Update Window Title
  352. SetWindowText(MPT_CFORMAT("Note Properties - Row {}, Channel {}")(row, chn + 1));
  353. CRect rectParent, rectWnd;
  354. parent->GetWindowRect(&rectParent);
  355. GetClientRect(&rectWnd);
  356. SetWindowPos(CMainFrame::GetMainFrame(),
  357. rectParent.left + (rectParent.Width() - rectWnd.right) / 2,
  358. rectParent.top + (rectParent.Height() - rectWnd.bottom) / 2,
  359. -1, -1, SWP_NOSIZE | SWP_NOACTIVATE);
  360. ShowWindow(SW_RESTORE);
  361. return true;
  362. }
  363. void CEditCommand::InitNote()
  364. {
  365. // Note
  366. cbnNote.SetRedraw(FALSE);
  367. if(oldSpecs != &sndFile.GetModSpecifications())
  368. {
  369. cbnNote.ResetContent();
  370. cbnNote.SetItemData(cbnNote.AddString(_T("No Note")), 0);
  371. AppendNotesToControlEx(cbnNote, sndFile, m->instr);
  372. oldSpecs = &sndFile.GetModSpecifications();
  373. }
  374. if(m->IsNote())
  375. {
  376. // Normal note / no note
  377. const ModCommand::NOTE noteStart = sndFile.GetModSpecifications().noteMin;
  378. cbnNote.SetCurSel(m->note - (noteStart - 1));
  379. } else if(m->note == NOTE_NONE)
  380. {
  381. cbnNote.SetCurSel(0);
  382. } else
  383. {
  384. // Special notes
  385. for(int i = cbnNote.GetCount() - 1; i >= 0; --i)
  386. {
  387. if(cbnNote.GetItemData(i) == m->note)
  388. {
  389. cbnNote.SetCurSel(i);
  390. break;
  391. }
  392. }
  393. }
  394. cbnNote.SetRedraw(TRUE);
  395. // Instrument
  396. cbnInstr.SetRedraw(FALSE);
  397. cbnInstr.ResetContent();
  398. if(m->IsPcNote())
  399. {
  400. // control plugin param note
  401. cbnInstr.SetItemData(cbnInstr.AddString(_T("No Effect")), 0);
  402. AddPluginNamesToCombobox(cbnInstr, sndFile.m_MixPlugins, false);
  403. } else
  404. {
  405. // instrument / sample
  406. cbnInstr.SetItemData(cbnInstr.AddString(_T("No Instrument")), 0);
  407. const uint32 nmax = sndFile.GetNumInstruments() ? sndFile.GetNumInstruments() : sndFile.GetNumSamples();
  408. for(uint32 i = 1; i <= nmax; i++)
  409. {
  410. CString s = mpt::cfmt::val(i) + _T(": ");
  411. // instrument / sample
  412. if(sndFile.GetNumInstruments())
  413. {
  414. if(sndFile.Instruments[i])
  415. s += mpt::ToCString(sndFile.GetCharsetInternal(), sndFile.Instruments[i]->name);
  416. } else
  417. s += mpt::ToCString(sndFile.GetCharsetInternal(), sndFile.m_szNames[i]);
  418. cbnInstr.SetItemData(cbnInstr.AddString(s), i);
  419. }
  420. }
  421. cbnInstr.SetCurSel(m->instr);
  422. cbnInstr.SetRedraw(TRUE);
  423. }
  424. void CEditCommand::InitVolume()
  425. {
  426. cbnVolCmd.SetRedraw(FALSE);
  427. cbnVolCmd.ResetContent();
  428. if(sndFile.GetType() == MOD_TYPE_MOD || m->IsPcNote())
  429. {
  430. cbnVolCmd.EnableWindow(FALSE);
  431. sldVolParam.EnableWindow(FALSE);
  432. } else
  433. {
  434. // Normal volume column effect
  435. cbnVolCmd.EnableWindow(TRUE);
  436. sldVolParam.EnableWindow(TRUE);
  437. uint32 count = effectInfo.GetNumVolCmds();
  438. cbnVolCmd.SetItemData(cbnVolCmd.AddString(_T(" None")), (DWORD_PTR)-1);
  439. cbnVolCmd.SetCurSel(0);
  440. UINT fxndx = effectInfo.GetIndexFromVolCmd(m->volcmd);
  441. for(uint32 i = 0; i < count; i++)
  442. {
  443. CString s;
  444. if(effectInfo.GetVolCmdInfo(i, &s))
  445. {
  446. int k = cbnVolCmd.AddString(s);
  447. cbnVolCmd.SetItemData(k, i);
  448. if(i == fxndx)
  449. cbnVolCmd.SetCurSel(k);
  450. }
  451. }
  452. UpdateVolCmdRange();
  453. }
  454. cbnVolCmd.SetRedraw(TRUE);
  455. }
  456. void CEditCommand::InitEffect()
  457. {
  458. if(m->IsPcNote())
  459. {
  460. cbnCommand.ShowWindow(SW_HIDE);
  461. return;
  462. }
  463. cbnCommand.ShowWindow(SW_SHOW);
  464. xParam = 0;
  465. xMultiplier = 1;
  466. getXParam(m->command, editPattern, editRow, editChannel, sndFile, xParam, xMultiplier);
  467. cbnCommand.SetRedraw(FALSE);
  468. cbnCommand.ResetContent();
  469. uint32 numfx = effectInfo.GetNumEffects();
  470. uint32 fxndx = effectInfo.GetIndexFromEffect(m->command, m->param);
  471. cbnCommand.SetItemData(cbnCommand.AddString(_T(" None")), (DWORD_PTR)-1);
  472. if(m->command == CMD_NONE)
  473. cbnCommand.SetCurSel(0);
  474. CString s;
  475. for(uint32 i = 0; i < numfx; i++)
  476. {
  477. if(effectInfo.GetEffectInfo(i, &s, true))
  478. {
  479. int k = cbnCommand.AddString(s);
  480. cbnCommand.SetItemData(k, i);
  481. if(i == fxndx)
  482. cbnCommand.SetCurSel(k);
  483. }
  484. }
  485. UpdateEffectRange(false);
  486. cbnCommand.SetRedraw(TRUE);
  487. cbnCommand.Invalidate();
  488. }
  489. void CEditCommand::InitPlugParam()
  490. {
  491. if(!m->IsPcNote())
  492. {
  493. cbnPlugParam.ShowWindow(SW_HIDE);
  494. return;
  495. }
  496. cbnPlugParam.ShowWindow(SW_SHOW);
  497. cbnPlugParam.SetRedraw(FALSE);
  498. cbnPlugParam.ResetContent();
  499. if(m->instr > 0 && m->instr <= MAX_MIXPLUGINS)
  500. {
  501. AddPluginParameternamesToCombobox(cbnPlugParam, sndFile.m_MixPlugins[m->instr - 1]);
  502. cbnPlugParam.SetCurSel(m->GetValueVolCol());
  503. }
  504. UpdateEffectRange(false);
  505. cbnPlugParam.SetRedraw(TRUE);
  506. cbnPlugParam.Invalidate();
  507. }
  508. void CEditCommand::UpdateVolCmdRange()
  509. {
  510. ModCommand::VOL rangeMin = 0, rangeMax = 0;
  511. LONG fxndx = effectInfo.GetIndexFromVolCmd(m->volcmd);
  512. bool ok = effectInfo.GetVolCmdInfo(fxndx, NULL, &rangeMin, &rangeMax);
  513. if(ok && rangeMax > rangeMin)
  514. {
  515. sldVolParam.EnableWindow(TRUE);
  516. sldVolParam.SetRange(rangeMin, rangeMax);
  517. Limit(m->vol, rangeMin, rangeMax);
  518. sldVolParam.SetPos(m->vol);
  519. } else
  520. {
  521. // Why does this not update the display at all?
  522. sldVolParam.SetRange(0, 0);
  523. sldVolParam.SetPos(0);
  524. sldVolParam.EnableWindow(FALSE);
  525. }
  526. UpdateVolCmdValue();
  527. }
  528. void CEditCommand::UpdateEffectRange(bool set)
  529. {
  530. DWORD pos;
  531. bool enable = true;
  532. if(m->IsPcNote())
  533. {
  534. // plugin param control note
  535. sldParam.SetRange(0, ModCommand::maxColumnValue);
  536. pos = m->GetValueEffectCol();
  537. } else
  538. {
  539. // process as effect
  540. ModCommand::PARAM rangeMin = 0, rangeMax = 0;
  541. LONG fxndx = effectInfo.GetIndexFromEffect(m->command, m->param);
  542. enable = ((fxndx >= 0) && (effectInfo.GetEffectInfo(fxndx, NULL, false, &rangeMin, &rangeMax)));
  543. pos = effectInfo.MapValueToPos(fxndx, m->param);
  544. if(pos > rangeMax)
  545. pos = rangeMin | (pos & 0x0F);
  546. Limit(pos, rangeMin, rangeMax);
  547. sldParam.SetRange(rangeMin, rangeMax);
  548. }
  549. if(enable)
  550. {
  551. sldParam.EnableWindow(TRUE);
  552. sldParam.SetPageSize(1);
  553. sldParam.SetPos(pos);
  554. } else
  555. {
  556. // Why does this not update the display at all?
  557. sldParam.SetRange(0, 0);
  558. sldParam.SetPos(0);
  559. sldParam.EnableWindow(FALSE);
  560. }
  561. UpdateEffectValue(set);
  562. }
  563. void CEditCommand::OnNoteChanged()
  564. {
  565. const bool wasParamControl = m->IsPcNote();
  566. ModCommand::NOTE newNote = m->note;
  567. ModCommand::INSTR newInstr = m->instr;
  568. int n = cbnNote.GetCurSel();
  569. if(n >= 0)
  570. newNote = static_cast<ModCommand::NOTE>(cbnNote.GetItemData(n));
  571. n = cbnInstr.GetCurSel();
  572. if(n >= 0)
  573. newInstr = static_cast<ModCommand::INSTR>(cbnInstr.GetItemData(n));
  574. if(m->note != newNote || m->instr != newInstr)
  575. {
  576. PrepareUndo("Note Entry");
  577. CModDoc *modDoc = sndFile.GetpModDoc();
  578. m->note = newNote;
  579. m->instr = newInstr;
  580. modDoc->UpdateAllViews(nullptr, RowHint(editRow), nullptr);
  581. if(wasParamControl != m->IsPcNote())
  582. {
  583. InitAll();
  584. } else if(!m->IsPcNote()
  585. && m->instr <= sndFile.GetNumInstruments()
  586. && newInstr <= sndFile.GetNumInstruments()
  587. && sndFile.Instruments[m->instr] != nullptr
  588. && sndFile.Instruments[newInstr] != nullptr
  589. && sndFile.Instruments[newInstr]->pTuning != sndFile.Instruments[m->instr]->pTuning)
  590. {
  591. //Checking whether note names should be recreated.
  592. InitNote();
  593. } else if(m->IsPcNote())
  594. {
  595. // Update parameter list
  596. InitPlugParam();
  597. }
  598. }
  599. }
  600. void CEditCommand::OnVolCmdChanged()
  601. {
  602. ModCommand::VOLCMD newVolCmd = m->volcmd;
  603. ModCommand::VOL newVol = m->vol;
  604. int n = cbnVolCmd.GetCurSel();
  605. if(n >= 0)
  606. {
  607. newVolCmd = effectInfo.GetVolCmdFromIndex(static_cast<UINT>(cbnVolCmd.GetItemData(n)));
  608. }
  609. newVol = static_cast<ModCommand::VOL>(sldVolParam.GetPos());
  610. const bool volCmdChanged = m->volcmd != newVolCmd;
  611. if(volCmdChanged || m->vol != newVol)
  612. {
  613. PrepareUndo("Volume Entry");
  614. CModDoc *modDoc = sndFile.GetpModDoc();
  615. m->volcmd = newVolCmd;
  616. m->vol = newVol;
  617. modDoc->UpdateAllViews(nullptr, RowHint(editRow), nullptr);
  618. if(volCmdChanged)
  619. UpdateVolCmdRange();
  620. else
  621. UpdateVolCmdValue();
  622. }
  623. }
  624. void CEditCommand::OnCommandChanged()
  625. {
  626. ModCommand::COMMAND newCommand = m->command;
  627. ModCommand::PARAM newParam = m->param;
  628. int n = cbnCommand.GetCurSel();
  629. if(n >= 0)
  630. {
  631. int ndx = static_cast<int>(cbnCommand.GetItemData(n));
  632. newCommand = static_cast<ModCommand::COMMAND>((ndx >= 0) ? effectInfo.GetEffectFromIndex(ndx, newParam) : CMD_NONE);
  633. }
  634. if(m->command != newCommand || m->param != newParam)
  635. {
  636. PrepareUndo("Effect Entry");
  637. m->command = newCommand;
  638. if(newCommand != CMD_NONE)
  639. {
  640. m->param = newParam;
  641. }
  642. xParam = 0;
  643. xMultiplier = 1;
  644. if(newCommand == CMD_XPARAM || mpt::contains(ExtendedCommands, newCommand))
  645. {
  646. getXParam(newCommand, editPattern, editRow, editChannel, sndFile, xParam, xMultiplier);
  647. }
  648. UpdateEffectRange(true);
  649. sndFile.GetpModDoc()->UpdateAllViews(nullptr, RowHint(editRow), nullptr);
  650. }
  651. }
  652. void CEditCommand::OnPlugParamChanged()
  653. {
  654. uint16 newPlugParam = m->GetValueVolCol();
  655. int n = cbnPlugParam.GetCurSel();
  656. if(n >= 0)
  657. {
  658. newPlugParam = static_cast<uint16>(cbnPlugParam.GetItemData(n));
  659. }
  660. if(m->GetValueVolCol() != newPlugParam)
  661. {
  662. PrepareUndo("Effect Entry");
  663. m->SetValueVolCol(newPlugParam);
  664. sndFile.GetpModDoc()->UpdateAllViews(nullptr, RowHint(editRow), nullptr);
  665. }
  666. }
  667. void CEditCommand::UpdateVolCmdValue()
  668. {
  669. CString s;
  670. if(m->IsPcNote())
  671. {
  672. // plugin param control note
  673. uint16 plugParam = static_cast<uint16>(sldVolParam.GetPos());
  674. s.Format(_T("Value: %u"), plugParam);
  675. } else
  676. {
  677. // process as effect
  678. effectInfo.GetVolCmdParamInfo(*m, &s);
  679. }
  680. SetDlgItemText(IDC_TEXT2, s);
  681. }
  682. void CEditCommand::UpdateEffectValue(bool set)
  683. {
  684. CString s;
  685. uint16 newPlugParam = 0;
  686. ModCommand::PARAM newParam = 0;
  687. if(m->IsPcNote())
  688. {
  689. // plugin param control note
  690. newPlugParam = static_cast<uint16>(sldParam.GetPos());
  691. s.Format(_T("Value: %u"), newPlugParam);
  692. } else
  693. {
  694. // process as effect
  695. LONG fxndx = effectInfo.GetIndexFromEffect(m->command, m->param);
  696. if(fxndx >= 0)
  697. {
  698. newParam = static_cast<ModCommand::PARAM>(effectInfo.MapPosToValue(fxndx, sldParam.GetPos()));
  699. effectInfo.GetEffectNameEx(s, *m, newParam * xMultiplier + xParam, editChannel);
  700. }
  701. }
  702. SetDlgItemText(IDC_TEXT1, s);
  703. if(set)
  704. {
  705. if((!m->IsPcNote() && m->param != newParam)
  706. || (m->IsPcNote() && m->GetValueVolCol() != newPlugParam))
  707. {
  708. PrepareUndo("Effect Entry");
  709. CModDoc *modDoc = sndFile.GetpModDoc();
  710. if(m->IsPcNote())
  711. {
  712. m->SetValueEffectCol(newPlugParam);
  713. } else
  714. {
  715. m->param = newParam;
  716. }
  717. modDoc->UpdateAllViews(nullptr, RowHint(editRow), nullptr);
  718. }
  719. }
  720. }
  721. void CEditCommand::PrepareUndo(const char *description)
  722. {
  723. CModDoc *modDoc = sndFile.GetpModDoc();
  724. if(!modified)
  725. {
  726. // Let's create just one undo step.
  727. modDoc->GetPatternUndo().PrepareUndo(editPattern, editChannel, editRow, 1, 1, description);
  728. modified = true;
  729. }
  730. modDoc->SetModified();
  731. }
  732. void CEditCommand::OnHScroll(UINT, UINT, CScrollBar *bar)
  733. {
  734. if(bar == static_cast<CWnd *>(&sldVolParam))
  735. {
  736. OnVolCmdChanged();
  737. } else if(bar == static_cast<CWnd *>(&sldParam))
  738. {
  739. UpdateEffectValue(true);
  740. }
  741. }
  742. void CEditCommand::OnActivate(UINT nState, CWnd *pWndOther, BOOL bMinimized)
  743. {
  744. CDialog::OnActivate(nState, pWndOther, bMinimized);
  745. if(nState == WA_INACTIVE)
  746. ShowWindow(SW_HIDE);
  747. }
  748. ////////////////////////////////////////////////////////////////////////////////////////////
  749. // Chord Editor
  750. BEGIN_MESSAGE_MAP(CChordEditor, ResizableDialog)
  751. ON_MESSAGE(WM_MOD_KBDNOTIFY, &CChordEditor::OnKeyboardNotify)
  752. ON_CBN_SELCHANGE(IDC_COMBO1, &CChordEditor::OnChordChanged)
  753. ON_CBN_SELCHANGE(IDC_COMBO2, &CChordEditor::OnBaseNoteChanged)
  754. ON_CBN_SELCHANGE(IDC_COMBO3, &CChordEditor::OnNote1Changed)
  755. ON_CBN_SELCHANGE(IDC_COMBO4, &CChordEditor::OnNote2Changed)
  756. ON_CBN_SELCHANGE(IDC_COMBO5, &CChordEditor::OnNote3Changed)
  757. END_MESSAGE_MAP()
  758. void CChordEditor::DoDataExchange(CDataExchange *pDX)
  759. {
  760. ResizableDialog::DoDataExchange(pDX);
  761. //{{AFX_DATA_MAP(CChordEditor)
  762. DDX_Control(pDX, IDC_KEYBOARD1, m_Keyboard);
  763. DDX_Control(pDX, IDC_COMBO1, m_CbnShortcut);
  764. DDX_Control(pDX, IDC_COMBO2, m_CbnBaseNote);
  765. DDX_Control(pDX, IDC_COMBO3, m_CbnNote[0]);
  766. DDX_Control(pDX, IDC_COMBO4, m_CbnNote[1]);
  767. DDX_Control(pDX, IDC_COMBO5, m_CbnNote[2]);
  768. static_assert(mpt::array_size<decltype(m_CbnNote)>::size == 3);
  769. //}}AFX_DATA_MAP
  770. }
  771. CChordEditor::CChordEditor(CWnd *parent)
  772. : ResizableDialog(IDD_CHORDEDIT, parent)
  773. {
  774. m_chords = TrackerSettings::GetChords();
  775. }
  776. BOOL CChordEditor::OnInitDialog()
  777. {
  778. ResizableDialog::OnInitDialog();
  779. m_Keyboard.Init(this, (CHORD_MAX - CHORD_MIN) / 12, true);
  780. m_CbnShortcut.SetRedraw(FALSE);
  781. m_CbnBaseNote.SetRedraw(FALSE);
  782. for(auto &combo : m_CbnNote)
  783. combo.SetRedraw(FALSE);
  784. // Shortcut key combo box
  785. AppendNotesToControl(m_CbnShortcut, NOTE_MIN, NOTE_MIN + static_cast<int>(kcVPEndChords) - static_cast<int>(kcVPStartChords));
  786. m_CbnShortcut.SetCurSel(0);
  787. // Base Note combo box
  788. m_CbnBaseNote.SetItemData(m_CbnBaseNote.AddString(_T("Relative")), MPTChord::relativeMode);
  789. AppendNotesToControl(m_CbnBaseNote, NOTE_MIN, NOTE_MIN + 3 * 12 - 1);
  790. // Chord Note combo boxes
  791. CString s;
  792. for(int note = CHORD_MIN - 1; note < CHORD_MAX; note++)
  793. {
  794. int noteVal = note;
  795. if(note == CHORD_MIN - 1)
  796. {
  797. s = _T("--");
  798. noteVal = MPTChord::noNote;
  799. } else
  800. {
  801. s = mpt::ToCString(CSoundFile::GetDefaultNoteName(mpt::wrapping_modulo(note, 12)));
  802. const int octave = mpt::wrapping_divide(note, 12);
  803. if(octave > 0)
  804. s.AppendFormat(_T(" (+%d)"), octave);
  805. else if(octave < 0)
  806. s.AppendFormat(_T(" (%d)"), octave);
  807. }
  808. for(auto &combo : m_CbnNote)
  809. combo.SetItemData(combo.AddString(s), noteVal);
  810. }
  811. m_CbnShortcut.SetRedraw(TRUE);
  812. m_CbnBaseNote.SetRedraw(TRUE);
  813. for(auto &combo : m_CbnNote)
  814. combo.SetRedraw(TRUE);
  815. // Update Dialog
  816. OnChordChanged();
  817. return TRUE;
  818. }
  819. void CChordEditor::OnOK()
  820. {
  821. TrackerSettings::GetChords() = m_chords;
  822. ResizableDialog::OnOK();
  823. }
  824. MPTChord &CChordEditor::GetChord()
  825. {
  826. int chord = m_CbnShortcut.GetCurSel();
  827. if(chord >= 0)
  828. chord = static_cast<int>(m_CbnShortcut.GetItemData(chord)) - NOTE_MIN;
  829. if(chord < 0 || chord >= static_cast<int>(m_chords.size()))
  830. chord = 0;
  831. return m_chords[chord];
  832. }
  833. LRESULT CChordEditor::OnKeyboardNotify(WPARAM cmd, LPARAM nKey)
  834. {
  835. const bool outside = static_cast<int>(nKey) == -1;
  836. if(cmd == KBDNOTIFY_LBUTTONUP && outside)
  837. {
  838. // Stopped dragging ouside of keyboard area
  839. m_mouseDownKey = m_dragKey = MPTChord::noNote;
  840. return 0;
  841. } else if (cmd == KBDNOTIFY_MOUSEMOVE || outside)
  842. {
  843. return 0;
  844. }
  845. MPTChord &chord = GetChord();
  846. const MPTChord::NoteType key = static_cast<MPTChord::NoteType>(nKey) + CHORD_MIN;
  847. bool update = false;
  848. if(cmd == KBDNOTIFY_LBUTTONDOWN && m_mouseDownKey == MPTChord::noNote)
  849. {
  850. // Initial mouse down
  851. m_mouseDownKey = key;
  852. m_dragKey = MPTChord::noNote;
  853. return 0;
  854. }
  855. if(cmd == KBDNOTIFY_LBUTTONDOWN && m_dragKey == MPTChord::noNote && key != m_mouseDownKey)
  856. {
  857. // Start dragging
  858. m_dragKey = m_mouseDownKey;
  859. }
  860. // Remove dragged note or toggle
  861. bool noteIsSet = false;
  862. for(auto &note : chord.notes)
  863. {
  864. if((m_dragKey != MPTChord::noNote && note == m_dragKey)
  865. || (m_dragKey == MPTChord::noNote && note == m_mouseDownKey))
  866. {
  867. note = MPTChord::noNote;
  868. noteIsSet = update = true;
  869. break;
  870. }
  871. }
  872. // Move or toggle note
  873. if(cmd != KBDNOTIFY_LBUTTONUP || m_dragKey != MPTChord::noNote || !noteIsSet)
  874. {
  875. for(auto &note : chord.notes)
  876. {
  877. if(note == MPTChord::noNote)
  878. {
  879. note = key;
  880. update = true;
  881. break;
  882. }
  883. }
  884. }
  885. if(cmd == KBDNOTIFY_LBUTTONUP)
  886. m_mouseDownKey = m_dragKey = MPTChord::noNote;
  887. else
  888. m_dragKey = key;
  889. if(update)
  890. {
  891. std::sort(chord.notes.begin(), chord.notes.end(), [](MPTChord::NoteType left, MPTChord::NoteType right)
  892. {
  893. return (left == MPTChord::noNote) ? false : (left < right);
  894. });
  895. OnChordChanged();
  896. }
  897. return 0;
  898. }
  899. void CChordEditor::OnChordChanged()
  900. {
  901. const MPTChord &chord = GetChord();
  902. if(chord.key != MPTChord::relativeMode)
  903. m_CbnBaseNote.SetCurSel(chord.key + 1);
  904. else
  905. m_CbnBaseNote.SetCurSel(0);
  906. for(int i = 0; i < MPTChord::notesPerChord - 1; i++)
  907. {
  908. int note = chord.notes[i];
  909. if(note == MPTChord::noNote)
  910. note = 0;
  911. else
  912. note += 1 - CHORD_MIN;
  913. m_CbnNote[i].SetCurSel(note);
  914. }
  915. UpdateKeyboard();
  916. }
  917. void CChordEditor::UpdateKeyboard()
  918. {
  919. MPTChord &chord = GetChord();
  920. const int baseNote = (chord.key == MPTChord::relativeMode) ? 0 : (chord.key % 12);
  921. for(int i = CHORD_MIN; i < CHORD_MAX; i++)
  922. {
  923. uint8 b = CKeyboardControl::KEYFLAG_NORMAL;
  924. for(const auto note : chord.notes)
  925. {
  926. if(i == note)
  927. b |= CKeyboardControl::KEYFLAG_REDDOT;
  928. }
  929. if(i == baseNote)
  930. b |= CKeyboardControl::KEYFLAG_BRIGHTDOT;
  931. m_Keyboard.SetFlags(i - CHORD_MIN, b);
  932. }
  933. m_Keyboard.InvalidateRect(nullptr, FALSE);
  934. }
  935. void CChordEditor::OnBaseNoteChanged()
  936. {
  937. MPTChord &chord = GetChord();
  938. int basenote = static_cast<int>(m_CbnBaseNote.GetItemData(m_CbnBaseNote.GetCurSel()));
  939. if(basenote != MPTChord::relativeMode)
  940. basenote -= NOTE_MIN;
  941. chord.key = (uint8)basenote;
  942. UpdateKeyboard();
  943. }
  944. void CChordEditor::OnNoteChanged(int noteIndex)
  945. {
  946. MPTChord &chord = GetChord();
  947. int note = m_CbnNote[noteIndex].GetCurSel();
  948. if(note < 0)
  949. return;
  950. chord.notes[noteIndex] = static_cast<int8>(m_CbnNote[noteIndex].GetItemData(note));
  951. UpdateKeyboard();
  952. }
  953. ////////////////////////////////////////////////////////////////////////////////////////////
  954. // Keyboard Split Settings (pattern editor)
  955. BEGIN_MESSAGE_MAP(CSplitKeyboardSettings, CDialog)
  956. ON_CBN_SELCHANGE(IDC_COMBO_OCTAVEMODIFIER, &CSplitKeyboardSettings::OnOctaveModifierChanged)
  957. END_MESSAGE_MAP()
  958. void CSplitKeyboardSettings::DoDataExchange(CDataExchange *pDX)
  959. {
  960. CDialog::DoDataExchange(pDX);
  961. //{{AFX_DATA_MAP(CSplitKeyboadSettings)
  962. DDX_Control(pDX, IDC_COMBO_SPLITINSTRUMENT, m_CbnSplitInstrument);
  963. DDX_Control(pDX, IDC_COMBO_SPLITNOTE, m_CbnSplitNote);
  964. DDX_Control(pDX, IDC_COMBO_OCTAVEMODIFIER, m_CbnOctaveModifier);
  965. DDX_Control(pDX, IDC_COMBO_SPLITVOLUME, m_CbnSplitVolume);
  966. //}}AFX_DATA_MAP
  967. }
  968. BOOL CSplitKeyboardSettings::OnInitDialog()
  969. {
  970. if(sndFile.GetpModDoc() == nullptr)
  971. return TRUE;
  972. CDialog::OnInitDialog();
  973. CString s;
  974. // Split Notes
  975. AppendNotesToControl(m_CbnSplitNote, sndFile.GetModSpecifications().noteMin, sndFile.GetModSpecifications().noteMax);
  976. m_CbnSplitNote.SetCurSel(m_Settings.splitNote - (sndFile.GetModSpecifications().noteMin - NOTE_MIN));
  977. // Octave modifier
  978. m_CbnOctaveModifier.SetRedraw(FALSE);
  979. m_CbnSplitVolume.InitStorage(SplitKeyboardSettings::splitOctaveRange * 2 + 1, 9);
  980. for(int i = -SplitKeyboardSettings::splitOctaveRange; i < SplitKeyboardSettings::splitOctaveRange + 1; i++)
  981. {
  982. s.Format(i < 0 ? _T("Octave -%d") : i > 0 ? _T("Octave +%d") : _T("No Change"), std::abs(i));
  983. int n = m_CbnOctaveModifier.AddString(s);
  984. m_CbnOctaveModifier.SetItemData(n, i);
  985. }
  986. m_CbnOctaveModifier.SetRedraw(TRUE);
  987. m_CbnOctaveModifier.SetCurSel(m_Settings.octaveModifier + SplitKeyboardSettings::splitOctaveRange);
  988. CheckDlgButton(IDC_PATTERN_OCTAVELINK, (m_Settings.octaveLink && m_Settings.octaveModifier != 0) ? BST_CHECKED : BST_UNCHECKED);
  989. // Volume
  990. m_CbnSplitVolume.SetRedraw(FALSE);
  991. m_CbnSplitVolume.InitStorage(65, 4);
  992. m_CbnSplitVolume.AddString(_T("No Change"));
  993. m_CbnSplitVolume.SetItemData(0, 0);
  994. for(int i = 1; i <= 64; i++)
  995. {
  996. s.Format(_T("%d"), i);
  997. int n = m_CbnSplitVolume.AddString(s);
  998. m_CbnSplitVolume.SetItemData(n, i);
  999. }
  1000. m_CbnSplitVolume.SetRedraw(TRUE);
  1001. m_CbnSplitVolume.SetCurSel(m_Settings.splitVolume);
  1002. // Instruments
  1003. m_CbnSplitInstrument.SetRedraw(FALSE);
  1004. m_CbnSplitInstrument.InitStorage(1 + (sndFile.GetNumInstruments() ? sndFile.GetNumInstruments() : sndFile.GetNumSamples()), 16);
  1005. m_CbnSplitInstrument.SetItemData(m_CbnSplitInstrument.AddString(_T("No Change")), 0);
  1006. if(sndFile.GetNumInstruments())
  1007. {
  1008. for(INSTRUMENTINDEX nIns = 1; nIns <= sndFile.GetNumInstruments(); nIns++)
  1009. {
  1010. if(sndFile.Instruments[nIns] == nullptr)
  1011. continue;
  1012. CString displayName = sndFile.GetpModDoc()->GetPatternViewInstrumentName(nIns);
  1013. int n = m_CbnSplitInstrument.AddString(displayName);
  1014. m_CbnSplitInstrument.SetItemData(n, nIns);
  1015. }
  1016. } else
  1017. {
  1018. for(SAMPLEINDEX nSmp = 1; nSmp <= sndFile.GetNumSamples(); nSmp++)
  1019. {
  1020. if(sndFile.GetSample(nSmp).HasSampleData())
  1021. {
  1022. s.Format(_T("%02d: "), nSmp);
  1023. s += mpt::ToCString(sndFile.GetCharsetInternal(), sndFile.m_szNames[nSmp]);
  1024. int n = m_CbnSplitInstrument.AddString(s);
  1025. m_CbnSplitInstrument.SetItemData(n, nSmp);
  1026. }
  1027. }
  1028. }
  1029. m_CbnSplitInstrument.SetRedraw(TRUE);
  1030. m_CbnSplitInstrument.SetCurSel(m_Settings.splitInstrument);
  1031. return TRUE;
  1032. }
  1033. void CSplitKeyboardSettings::OnOK()
  1034. {
  1035. CDialog::OnOK();
  1036. m_Settings.splitNote = static_cast<ModCommand::NOTE>(m_CbnSplitNote.GetItemData(m_CbnSplitNote.GetCurSel()) - 1);
  1037. m_Settings.octaveModifier = m_CbnOctaveModifier.GetCurSel() - SplitKeyboardSettings::splitOctaveRange;
  1038. m_Settings.octaveLink = (IsDlgButtonChecked(IDC_PATTERN_OCTAVELINK) != BST_UNCHECKED);
  1039. m_Settings.splitVolume = static_cast<ModCommand::VOL>(m_CbnSplitVolume.GetCurSel());
  1040. m_Settings.splitInstrument = static_cast<ModCommand::INSTR>(m_CbnSplitInstrument.GetItemData(m_CbnSplitInstrument.GetCurSel()));
  1041. }
  1042. void CSplitKeyboardSettings::OnCancel()
  1043. {
  1044. CDialog::OnCancel();
  1045. }
  1046. void CSplitKeyboardSettings::OnOctaveModifierChanged()
  1047. {
  1048. CheckDlgButton(IDC_PATTERN_OCTAVELINK, (m_CbnOctaveModifier.GetCurSel() != 9) ? BST_CHECKED : BST_UNCHECKED);
  1049. }
  1050. /////////////////////////////////////////////////////////////////////////
  1051. // Show channel properties from pattern editor
  1052. BEGIN_MESSAGE_MAP(QuickChannelProperties, CDialog)
  1053. ON_WM_HSCROLL() // Sliders
  1054. ON_WM_ACTIVATE() // Catch Window focus change
  1055. ON_EN_UPDATE(IDC_EDIT1, &QuickChannelProperties::OnVolChanged)
  1056. ON_EN_UPDATE(IDC_EDIT2, &QuickChannelProperties::OnPanChanged)
  1057. ON_EN_UPDATE(IDC_EDIT3, &QuickChannelProperties::OnNameChanged)
  1058. ON_COMMAND(IDC_CHECK1, &QuickChannelProperties::OnMuteChanged)
  1059. ON_COMMAND(IDC_CHECK2, &QuickChannelProperties::OnSurroundChanged)
  1060. ON_COMMAND(IDC_BUTTON1, &QuickChannelProperties::OnPrevChannel)
  1061. ON_COMMAND(IDC_BUTTON2, &QuickChannelProperties::OnNextChannel)
  1062. ON_COMMAND(IDC_BUTTON3, &QuickChannelProperties::OnChangeColor)
  1063. ON_COMMAND(IDC_BUTTON4, &QuickChannelProperties::OnChangeColor)
  1064. ON_COMMAND(IDC_BUTTON5, &QuickChannelProperties::OnPickPrevColor)
  1065. ON_COMMAND(IDC_BUTTON6, &QuickChannelProperties::OnPickNextColor)
  1066. ON_MESSAGE(WM_MOD_KEYCOMMAND, &QuickChannelProperties::OnCustomKeyMsg)
  1067. ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, &QuickChannelProperties::OnToolTipText)
  1068. END_MESSAGE_MAP()
  1069. void QuickChannelProperties::DoDataExchange(CDataExchange *pDX)
  1070. {
  1071. DDX_Control(pDX, IDC_SLIDER1, m_volSlider);
  1072. DDX_Control(pDX, IDC_SLIDER2, m_panSlider);
  1073. DDX_Control(pDX, IDC_SPIN1, m_volSpin);
  1074. DDX_Control(pDX, IDC_SPIN2, m_panSpin);
  1075. DDX_Control(pDX, IDC_EDIT3, m_nameEdit);
  1076. }
  1077. QuickChannelProperties::~QuickChannelProperties()
  1078. {
  1079. DestroyWindow();
  1080. }
  1081. void QuickChannelProperties::OnActivate(UINT nState, CWnd *, BOOL)
  1082. {
  1083. if(nState == WA_INACTIVE && !m_settingColor)
  1084. {
  1085. // Hide window when changing focus to another window.
  1086. m_visible = false;
  1087. ShowWindow(SW_HIDE);
  1088. }
  1089. }
  1090. // Show channel properties for a given channel at a given screen position.
  1091. void QuickChannelProperties::Show(CModDoc *modDoc, CHANNELINDEX chn, CPoint position)
  1092. {
  1093. if(!m_hWnd)
  1094. {
  1095. Create(IDD_CHANNELSETTINGS, nullptr);
  1096. EnableToolTips();
  1097. m_colorBtn.SubclassDlgItem(IDC_BUTTON4, this);
  1098. m_colorBtnPrev.SubclassDlgItem(IDC_BUTTON5, this);
  1099. m_colorBtnNext.SubclassDlgItem(IDC_BUTTON6, this);
  1100. m_volSlider.SetRange(0, 64);
  1101. m_volSlider.SetTicFreq(8);
  1102. m_volSpin.SetRange(0, 64);
  1103. m_panSlider.SetRange(0, 64);
  1104. m_panSlider.SetTicFreq(8);
  1105. m_panSpin.SetRange(0, 256);
  1106. m_nameEdit.SetFocus();
  1107. }
  1108. m_document = modDoc;
  1109. m_channel = chn;
  1110. SetParent(nullptr);
  1111. // Center window around point where user clicked.
  1112. CRect rect, screenRect;
  1113. GetWindowRect(rect);
  1114. ::GetWindowRect(::GetDesktopWindow(), &screenRect);
  1115. rect.MoveToXY(
  1116. Clamp(static_cast<int>(position.x) - rect.Width() / 2, 0, static_cast<int>(screenRect.right) - rect.Width()),
  1117. Clamp(static_cast<int>(position.y) - rect.Height() / 2, 0, static_cast<int>(screenRect.bottom) - rect.Height()));
  1118. MoveWindow(rect);
  1119. SetWindowText(MPT_TFORMAT("Settings for Channel {}")(chn + 1).c_str());
  1120. UpdateDisplay();
  1121. const BOOL enablePan = (m_document->GetModType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) ? FALSE : TRUE;
  1122. const BOOL itOnly = (m_document->GetModType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) ? TRUE : FALSE;
  1123. // Volume controls
  1124. m_volSlider.EnableWindow(itOnly);
  1125. m_volSpin.EnableWindow(itOnly);
  1126. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT1), itOnly);
  1127. // Pan controls
  1128. m_panSlider.EnableWindow(enablePan);
  1129. m_panSpin.EnableWindow(enablePan);
  1130. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT2), enablePan);
  1131. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_CHECK2), itOnly);
  1132. // Channel name
  1133. m_nameEdit.EnableWindow((m_document->GetModType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM)) ? TRUE : FALSE);
  1134. ShowWindow(SW_SHOW);
  1135. m_visible = true;
  1136. }
  1137. void QuickChannelProperties::UpdateDisplay()
  1138. {
  1139. // Set up channel properties
  1140. m_visible = false;
  1141. const ModChannelSettings &settings = m_document->GetSoundFile().ChnSettings[m_channel];
  1142. SetDlgItemInt(IDC_EDIT1, settings.nVolume, FALSE);
  1143. SetDlgItemInt(IDC_EDIT2, settings.nPan, FALSE);
  1144. m_volSlider.SetPos(settings.nVolume);
  1145. m_panSlider.SetPos(settings.nPan / 4u);
  1146. CheckDlgButton(IDC_CHECK1, (settings.dwFlags[CHN_MUTE]) ? TRUE : FALSE);
  1147. CheckDlgButton(IDC_CHECK2, (settings.dwFlags[CHN_SURROUND]) ? TRUE : FALSE);
  1148. TCHAR description[16];
  1149. wsprintf(description, _T("Channel %d:"), m_channel + 1);
  1150. SetDlgItemText(IDC_STATIC_CHANNEL_NAME, description);
  1151. m_nameEdit.LimitText(MAX_CHANNELNAME - 1);
  1152. m_nameEdit.SetWindowText(mpt::ToCString(m_document->GetSoundFile().GetCharsetInternal(), settings.szName));
  1153. const bool isFirst = (m_channel <= 0), isLast = (m_channel >= m_document->GetNumChannels() - 1);
  1154. m_colorBtn.SetColor(settings.color);
  1155. m_colorBtnPrev.EnableWindow(isFirst ? FALSE : TRUE);
  1156. if(!isFirst)
  1157. m_colorBtnPrev.SetColor(m_document->GetSoundFile().ChnSettings[m_channel - 1].color);
  1158. m_colorBtnNext.EnableWindow(isLast ? FALSE : TRUE);
  1159. if(!isLast)
  1160. m_colorBtnNext.SetColor(m_document->GetSoundFile().ChnSettings[m_channel + 1].color);
  1161. m_settingsChanged = false;
  1162. m_visible = true;
  1163. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_BUTTON1), isFirst ? FALSE : TRUE);
  1164. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_BUTTON2), isLast ? FALSE : TRUE);
  1165. }
  1166. void QuickChannelProperties::PrepareUndo()
  1167. {
  1168. if(!m_settingsChanged)
  1169. {
  1170. // Backup old channel settings through pattern undo.
  1171. m_settingsChanged = true;
  1172. m_document->GetPatternUndo().PrepareChannelUndo(m_channel, 1, "Channel Settings");
  1173. }
  1174. }
  1175. void QuickChannelProperties::OnVolChanged()
  1176. {
  1177. if(!m_visible)
  1178. {
  1179. return;
  1180. }
  1181. uint16 volume = static_cast<uint16>(GetDlgItemInt(IDC_EDIT1));
  1182. if(volume >= 0 && volume <= 64)
  1183. {
  1184. PrepareUndo();
  1185. m_document->SetChannelGlobalVolume(m_channel, volume);
  1186. m_volSlider.SetPos(volume);
  1187. m_document->UpdateAllViews(nullptr, GeneralHint(m_channel).Channels(), this);
  1188. }
  1189. }
  1190. void QuickChannelProperties::OnPanChanged()
  1191. {
  1192. if(!m_visible)
  1193. {
  1194. return;
  1195. }
  1196. uint16 panning = static_cast<uint16>(GetDlgItemInt(IDC_EDIT2));
  1197. if(panning >= 0 && panning <= 256)
  1198. {
  1199. PrepareUndo();
  1200. m_document->SetChannelDefaultPan(m_channel, panning);
  1201. m_panSlider.SetPos(panning / 4u);
  1202. m_document->UpdateAllViews(nullptr, GeneralHint(m_channel).Channels(), this);
  1203. // Surround is forced off when changing pan, so uncheck the checkbox.
  1204. CheckDlgButton(IDC_CHECK2, BST_UNCHECKED);
  1205. }
  1206. }
  1207. void QuickChannelProperties::OnHScroll(UINT, UINT, CScrollBar *bar)
  1208. {
  1209. if(!m_visible)
  1210. {
  1211. return;
  1212. }
  1213. bool update = false;
  1214. // Volume slider
  1215. if(bar == reinterpret_cast<CScrollBar *>(&m_volSlider))
  1216. {
  1217. uint16 pos = static_cast<uint16>(m_volSlider.GetPos());
  1218. PrepareUndo();
  1219. if(m_document->SetChannelGlobalVolume(m_channel, pos))
  1220. {
  1221. SetDlgItemInt(IDC_EDIT1, pos);
  1222. update = true;
  1223. }
  1224. }
  1225. // Pan slider
  1226. if(bar == reinterpret_cast<CScrollBar *>(&m_panSlider))
  1227. {
  1228. uint16 pos = static_cast<uint16>(m_panSlider.GetPos());
  1229. PrepareUndo();
  1230. if(m_document->SetChannelDefaultPan(m_channel, pos * 4u))
  1231. {
  1232. SetDlgItemInt(IDC_EDIT2, pos * 4u);
  1233. CheckDlgButton(IDC_CHECK2, BST_UNCHECKED);
  1234. update = true;
  1235. }
  1236. }
  1237. if(update)
  1238. {
  1239. m_document->UpdateAllViews(nullptr, GeneralHint(m_channel).Channels(), this);
  1240. }
  1241. }
  1242. void QuickChannelProperties::OnMuteChanged()
  1243. {
  1244. if(!m_visible)
  1245. {
  1246. return;
  1247. }
  1248. m_document->MuteChannel(m_channel, IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED);
  1249. m_document->UpdateAllViews(nullptr, GeneralHint(m_channel).Channels(), this);
  1250. }
  1251. void QuickChannelProperties::OnSurroundChanged()
  1252. {
  1253. if(!m_visible)
  1254. {
  1255. return;
  1256. }
  1257. PrepareUndo();
  1258. m_document->SurroundChannel(m_channel, IsDlgButtonChecked(IDC_CHECK2) != BST_UNCHECKED);
  1259. m_document->UpdateAllViews(nullptr, GeneralHint(m_channel).Channels(), this);
  1260. UpdateDisplay();
  1261. }
  1262. void QuickChannelProperties::OnNameChanged()
  1263. {
  1264. if(!m_visible)
  1265. {
  1266. return;
  1267. }
  1268. ModChannelSettings &settings = m_document->GetSoundFile().ChnSettings[m_channel];
  1269. CString newNameTmp;
  1270. m_nameEdit.GetWindowText(newNameTmp);
  1271. std::string newName = mpt::ToCharset(m_document->GetSoundFile().GetCharsetInternal(), newNameTmp);
  1272. if(newName != settings.szName)
  1273. {
  1274. PrepareUndo();
  1275. settings.szName = newName;
  1276. m_document->SetModified();
  1277. m_document->UpdateAllViews(nullptr, GeneralHint(m_channel).Channels(), this);
  1278. }
  1279. }
  1280. void QuickChannelProperties::OnChangeColor()
  1281. {
  1282. m_settingColor = true;
  1283. if(auto color = m_colorBtn.PickColor(m_document->GetSoundFile(), m_channel); color.has_value())
  1284. {
  1285. PrepareUndo();
  1286. m_document->GetSoundFile().ChnSettings[m_channel].color = *color;
  1287. if(m_document->SupportsChannelColors())
  1288. m_document->SetModified();
  1289. m_document->UpdateAllViews(nullptr, GeneralHint(m_channel).Channels(), this);
  1290. }
  1291. m_settingColor = false;
  1292. }
  1293. void QuickChannelProperties::OnPickPrevColor()
  1294. {
  1295. if(m_channel > 0)
  1296. PickColorFromChannel(m_channel - 1);
  1297. }
  1298. void QuickChannelProperties::OnPickNextColor()
  1299. {
  1300. if(m_channel < m_document->GetNumChannels() - 1)
  1301. PickColorFromChannel(m_channel + 1);
  1302. }
  1303. void QuickChannelProperties::PickColorFromChannel(CHANNELINDEX channel)
  1304. {
  1305. auto &channels = m_document->GetSoundFile().ChnSettings;
  1306. if(channels[channel].color != channels[m_channel].color)
  1307. {
  1308. PrepareUndo();
  1309. channels[m_channel].color = channels[channel].color;
  1310. m_colorBtn.SetColor(channels[m_channel].color);
  1311. if(m_document->SupportsChannelColors())
  1312. m_document->SetModified();
  1313. m_document->UpdateAllViews(nullptr, GeneralHint(m_channel).Channels(), this);
  1314. }
  1315. }
  1316. void QuickChannelProperties::OnPrevChannel()
  1317. {
  1318. if(m_channel > 0)
  1319. {
  1320. m_channel--;
  1321. UpdateDisplay();
  1322. }
  1323. }
  1324. void QuickChannelProperties::OnNextChannel()
  1325. {
  1326. if(m_channel < m_document->GetNumChannels() - 1)
  1327. {
  1328. m_channel++;
  1329. UpdateDisplay();
  1330. }
  1331. }
  1332. BOOL QuickChannelProperties::PreTranslateMessage(MSG *pMsg)
  1333. {
  1334. if(pMsg)
  1335. {
  1336. //We handle keypresses before Windows has a chance to handle them (for alt etc..)
  1337. if((pMsg->message == WM_SYSKEYUP) || (pMsg->message == WM_KEYUP) ||
  1338. (pMsg->message == WM_SYSKEYDOWN) || (pMsg->message == WM_KEYDOWN))
  1339. {
  1340. CInputHandler *ih = CMainFrame::GetInputHandler();
  1341. //Translate message manually
  1342. UINT nChar = static_cast<UINT>(pMsg->wParam);
  1343. UINT nRepCnt = LOWORD(pMsg->lParam);
  1344. UINT nFlags = HIWORD(pMsg->lParam);
  1345. KeyEventType kT = ih->GetKeyEventType(nFlags);
  1346. if(ih->KeyEvent(kCtxChannelSettings, nChar, nRepCnt, nFlags, kT, this) != kcNull)
  1347. {
  1348. return TRUE; // Mapped to a command, no need to pass message on.
  1349. }
  1350. }
  1351. }
  1352. return CDialog::PreTranslateMessage(pMsg);
  1353. }
  1354. LRESULT QuickChannelProperties::OnCustomKeyMsg(WPARAM wParam, LPARAM)
  1355. {
  1356. switch(wParam)
  1357. {
  1358. case kcChnSettingsPrev:
  1359. OnPrevChannel();
  1360. return wParam;
  1361. case kcChnSettingsNext:
  1362. OnNextChannel();
  1363. return wParam;
  1364. case kcChnColorFromPrev:
  1365. OnPickPrevColor();
  1366. return wParam;
  1367. case kcChnColorFromNext:
  1368. OnPickNextColor();
  1369. return wParam;
  1370. case kcChnSettingsClose:
  1371. OnActivate(WA_INACTIVE, nullptr, FALSE);
  1372. return wParam;
  1373. }
  1374. return kcNull;
  1375. }
  1376. BOOL QuickChannelProperties::OnToolTipText(UINT, NMHDR *pNMHDR, LRESULT *pResult)
  1377. {
  1378. auto pTTT = reinterpret_cast<TOOLTIPTEXT *>(pNMHDR);
  1379. UINT_PTR id = pNMHDR->idFrom;
  1380. if(pTTT->uFlags & TTF_IDISHWND)
  1381. {
  1382. // idFrom is actually the HWND of the tool
  1383. id = static_cast<UINT_PTR>(::GetDlgCtrlID(reinterpret_cast<HWND>(id)));
  1384. }
  1385. mpt::tstring text;
  1386. CommandID cmd = kcNull;
  1387. switch (id)
  1388. {
  1389. case IDC_EDIT1:
  1390. case IDC_SLIDER1:
  1391. text = CModDoc::LinearToDecibels(m_document->GetSoundFile().ChnSettings[m_channel].nVolume, 64.0);
  1392. break;
  1393. case IDC_EDIT2:
  1394. case IDC_SLIDER2:
  1395. text = CModDoc::PanningToString(m_document->GetSoundFile().ChnSettings[m_channel].nPan, 128);
  1396. break;
  1397. case IDC_BUTTON1:
  1398. text = _T("Previous Channel");
  1399. cmd = kcChnSettingsPrev;
  1400. break;
  1401. case IDC_BUTTON2:
  1402. text = _T("Next Channel");
  1403. cmd = kcChnSettingsNext;
  1404. break;
  1405. case IDC_BUTTON5:
  1406. text = _T("Take color from previous channel");
  1407. cmd = kcChnColorFromPrev;
  1408. break;
  1409. case IDC_BUTTON6:
  1410. text = _T("Take color from next channel");
  1411. cmd = kcChnColorFromNext;
  1412. break;
  1413. default:
  1414. return FALSE;
  1415. }
  1416. if(cmd != kcNull)
  1417. {
  1418. auto keyText = CMainFrame::GetInputHandler()->m_activeCommandSet->GetKeyTextFromCommand(cmd, 0);
  1419. if(!keyText.IsEmpty())
  1420. text += MPT_TFORMAT(" ({})")(keyText);
  1421. }
  1422. mpt::String::WriteWinBuf(pTTT->szText) = text;
  1423. *pResult = 0;
  1424. // bring the tooltip window above other popup windows
  1425. ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);
  1426. return TRUE; // message was handled
  1427. }
  1428. OPENMPT_NAMESPACE_END