view_com.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. /*
  2. * view_com.cpp
  3. * ------------
  4. * Purpose: Song comments tab, lower 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 "Childfrm.h"
  15. #include "Clipboard.h"
  16. #include "ImageLists.h"
  17. #include "Moddoc.h"
  18. #include "Globals.h"
  19. #include "Ctrl_com.h"
  20. #include "ChannelManagerDlg.h"
  21. #include "../common/mptStringBuffer.h"
  22. #include "view_com.h"
  23. #include "../soundlib/mod_specifications.h"
  24. OPENMPT_NAMESPACE_BEGIN
  25. #define DETAILS_TOOLBAR_CY Util::ScalePixels(28, m_hWnd)
  26. enum
  27. {
  28. SMPLIST_SAMPLENAME = 0,
  29. SMPLIST_SAMPLENO,
  30. SMPLIST_SIZE,
  31. SMPLIST_TYPE,
  32. SMPLIST_MIDDLEC,
  33. SMPLIST_INSTR,
  34. SMPLIST_FILENAME,
  35. SMPLIST_PATH,
  36. SMPLIST_COLUMNS
  37. };
  38. enum
  39. {
  40. INSLIST_INSTRUMENTNAME = 0,
  41. INSLIST_INSTRUMENTNO,
  42. INSLIST_SAMPLES,
  43. INSLIST_ENVELOPES,
  44. INSLIST_FILENAME,
  45. INSLIST_PLUGIN,
  46. INSLIST_COLUMNS
  47. };
  48. const CListCtrlEx::Header gSampleHeaders[SMPLIST_COLUMNS] =
  49. {
  50. { _T("Sample Name"), 192, LVCFMT_LEFT },
  51. { _T("Num"), 45, LVCFMT_RIGHT },
  52. { _T("Size"), 72, LVCFMT_RIGHT },
  53. { _T("Type"), 45, LVCFMT_RIGHT },
  54. { _T("C-5 Freq"), 80, LVCFMT_RIGHT },
  55. { _T("Instr"), 64, LVCFMT_RIGHT },
  56. { _T("File Name"), 128, LVCFMT_RIGHT },
  57. { _T("Path"), 256, LVCFMT_LEFT },
  58. };
  59. const CListCtrlEx::Header gInstrumentHeaders[INSLIST_COLUMNS] =
  60. {
  61. { _T("Instrument Name"), 192, LVCFMT_LEFT },
  62. { _T("Num"), 45, LVCFMT_RIGHT },
  63. { _T("Samples"), 64, LVCFMT_RIGHT },
  64. { _T("Envelopes"), 128, LVCFMT_RIGHT },
  65. { _T("File Name"), 128, LVCFMT_RIGHT },
  66. { _T("Plugin"), 128, LVCFMT_RIGHT },
  67. };
  68. IMPLEMENT_SERIAL(CViewComments, CModScrollView, 0)
  69. BEGIN_MESSAGE_MAP(CViewComments, CModScrollView)
  70. //{{AFX_MSG_MAP(CViewComments)
  71. ON_WM_SIZE()
  72. ON_WM_DESTROY()
  73. ON_MESSAGE(WM_MOD_KEYCOMMAND, &CViewComments::OnCustomKeyMsg)
  74. ON_MESSAGE(WM_MOD_MIDIMSG, &CViewComments::OnMidiMsg)
  75. ON_COMMAND(IDC_LIST_SAMPLES, &CViewComments::OnShowSamples)
  76. ON_COMMAND(IDC_LIST_INSTRUMENTS, &CViewComments::OnShowInstruments)
  77. ON_COMMAND(IDC_LIST_PATTERNS, &CViewComments::OnShowPatterns)
  78. ON_COMMAND(ID_COPY_ALL_NAMES, &CViewComments::OnCopyNames)
  79. ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_DETAILS, &CViewComments::OnEndLabelEdit)
  80. ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_DETAILS, &CViewComments::OnBeginLabelEdit)
  81. ON_NOTIFY(NM_DBLCLK, IDC_LIST_DETAILS, &CViewComments::OnDblClickListItem)
  82. ON_NOTIFY(NM_RCLICK, IDC_LIST_DETAILS, &CViewComments::OnRClickListItem)
  83. //}}AFX_MSG_MAP
  84. END_MESSAGE_MAP()
  85. void CViewComments::OnInitialUpdate()
  86. {
  87. CModScrollView::OnInitialUpdate();
  88. if(m_nListId == 0)
  89. {
  90. m_nListId = IDC_LIST_SAMPLES;
  91. // For XM, set the instrument list as the default list
  92. const CModDoc *pModDoc = GetDocument();
  93. if(pModDoc && pModDoc->GetSoundFile().GetMessageHeuristic() == ModMessageHeuristicOrder::InstrumentsSamples && pModDoc->GetNumInstruments() > 0)
  94. {
  95. m_nListId = IDC_LIST_INSTRUMENTS;
  96. }
  97. }
  98. CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
  99. CRect rect;
  100. if (pFrame)
  101. {
  102. COMMENTVIEWSTATE &commentState = pFrame->GetCommentViewState();
  103. if (commentState.initialized)
  104. {
  105. m_nListId = commentState.nId;
  106. }
  107. }
  108. GetClientRect(&rect);
  109. m_ToolBar.Create(WS_CHILD|WS_VISIBLE|CCS_NOPARENTALIGN, rect, this, IDC_TOOLBAR_DETAILS);
  110. m_ToolBar.Init(CMainFrame::GetMainFrame()->m_MiscIcons, CMainFrame::GetMainFrame()->m_MiscIconsDisabled);
  111. m_ItemList.Create(WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL | LVS_EDITLABELS | LVS_NOSORTHEADER, rect, this, IDC_LIST_DETAILS);
  112. m_ItemList.ModifyStyleEx(0, WS_EX_STATICEDGE);
  113. // Add ToolBar Buttons
  114. m_ToolBar.AddButton(IDC_LIST_SAMPLES, IMAGE_SAMPLES);
  115. m_ToolBar.AddButton(IDC_LIST_INSTRUMENTS, IMAGE_INSTRUMENTS);
  116. //m_ToolBar.AddButton(IDC_LIST_PATTERNS, TIMAGE_TAB_PATTERNS);
  117. m_ToolBar.SetIndent(4);
  118. UpdateButtonState();
  119. UpdateView(UpdateHint().ModType());
  120. }
  121. void CViewComments::OnDestroy()
  122. {
  123. if(m_lastNote != NOTE_NONE)
  124. GetDocument()->NoteOff(m_lastNote, true, m_noteInstr, m_noteChannel);
  125. CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
  126. if (pFrame)
  127. {
  128. COMMENTVIEWSTATE &commentState = pFrame->GetCommentViewState();
  129. commentState.initialized = true;
  130. commentState.nId = m_nListId;
  131. }
  132. CModScrollView::OnDestroy();
  133. }
  134. LRESULT CViewComments::OnModViewMsg(WPARAM wParam, LPARAM lParam)
  135. {
  136. switch(wParam)
  137. {
  138. case VIEWMSG_SETFOCUS:
  139. case VIEWMSG_SETACTIVE:
  140. GetParentFrame()->SetActiveView(this);
  141. m_ItemList.SetFocus();
  142. return 0;
  143. default:
  144. return CModScrollView::OnModViewMsg(wParam, lParam);
  145. }
  146. }
  147. LRESULT CViewComments::OnMidiMsg(WPARAM midiData_, LPARAM)
  148. {
  149. uint32 midiData = static_cast<uint32>(midiData_);
  150. // Handle MIDI messages assigned to shortcuts
  151. CInputHandler *ih = CMainFrame::GetInputHandler();
  152. ih->HandleMIDIMessage(kCtxViewComments, midiData) != kcNull
  153. || ih->HandleMIDIMessage(kCtxAllContexts, midiData) != kcNull;
  154. return 1;
  155. }
  156. LRESULT CViewComments::OnCustomKeyMsg(WPARAM wParam, LPARAM)
  157. {
  158. const int item = m_ItemList.GetSelectionMark() + 1;
  159. if(item == 0)
  160. return kcNull;
  161. auto modDoc = GetDocument();
  162. if(wParam >= kcCommentsStartNotes && wParam <= kcCommentsEndNotes)
  163. {
  164. const auto lastInstr = m_noteInstr;
  165. m_noteInstr = (m_nListId == IDC_LIST_SAMPLES) ? INSTRUMENTINDEX_INVALID : static_cast<INSTRUMENTINDEX>(item);
  166. const auto note = modDoc->GetNoteWithBaseOctave(static_cast<int>(wParam - kcCommentsStartNotes), m_noteInstr);
  167. PlayNoteParam params(note);
  168. if(m_nListId == IDC_LIST_SAMPLES)
  169. params.Sample(static_cast<SAMPLEINDEX>(item));
  170. else if(m_nListId == IDC_LIST_INSTRUMENTS)
  171. params.Instrument(m_noteInstr);
  172. else
  173. return kcNull;
  174. if(m_lastNote != NOTE_NONE)
  175. modDoc->NoteOff(m_lastNote, true, lastInstr, m_noteChannel);
  176. m_noteChannel = modDoc->PlayNote(params);
  177. m_lastNote = note;
  178. return wParam;
  179. } else if(wParam >= kcCommentsStartNoteStops && wParam <= kcCommentsEndNoteStops)
  180. {
  181. const auto note = modDoc->GetNoteWithBaseOctave(static_cast<int>(wParam - kcCommentsStartNoteStops), m_noteInstr);
  182. modDoc->NoteOff(note, false, m_noteInstr, m_noteChannel);
  183. return wParam;
  184. } else if(wParam == kcToggleSmpInsList)
  185. {
  186. bool ok = SwitchToList(m_nListId == IDC_LIST_SAMPLES ? IDC_LIST_INSTRUMENTS : IDC_LIST_SAMPLES);
  187. if(ok)
  188. {
  189. int newItem = 0;
  190. switch(m_nListId)
  191. {
  192. case IDC_LIST_SAMPLES:
  193. // Switch to a sample belonging to previously selected instrument
  194. if(SAMPLEINDEX smp = modDoc->FindInstrumentChild(static_cast<INSTRUMENTINDEX>(item)); smp != 0 && smp != SAMPLEINDEX_INVALID)
  195. newItem = smp - 1;
  196. break;
  197. case IDC_LIST_INSTRUMENTS:
  198. // Switch to parent instrument of previously selected sample
  199. if(INSTRUMENTINDEX ins = modDoc->FindSampleParent(static_cast<SAMPLEINDEX>(item)); ins != 0 && ins != INSTRUMENTINDEX_INVALID)
  200. newItem = ins - 1;
  201. break;
  202. }
  203. m_ItemList.SetItemState(newItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
  204. m_ItemList.SetSelectionMark(newItem);
  205. m_ItemList.EnsureVisible(newItem, FALSE);
  206. m_ItemList.SetFocus();
  207. }
  208. return wParam;
  209. } else if(wParam == kcExecuteSmpInsListItem)
  210. {
  211. OnDblClickListItem(nullptr, nullptr);
  212. return wParam;
  213. } else if(wParam == kcRenameSmpInsListItem)
  214. {
  215. m_ItemList.EditLabel(item - 1);
  216. return wParam;
  217. }
  218. return kcNull;
  219. }
  220. BOOL CViewComments::PreTranslateMessage(MSG *pMsg)
  221. {
  222. if(pMsg)
  223. {
  224. if((pMsg->message == WM_SYSKEYUP) || (pMsg->message == WM_KEYUP)
  225. || (pMsg->message == WM_SYSKEYDOWN) || (pMsg->message == WM_KEYDOWN))
  226. {
  227. CInputHandler *ih = CMainFrame::GetInputHandler();
  228. //Translate message manually
  229. UINT nChar = static_cast<UINT>(pMsg->wParam);
  230. UINT nRepCnt = LOWORD(pMsg->lParam);
  231. UINT nFlags = HIWORD(pMsg->lParam);
  232. KeyEventType kT = ih->GetKeyEventType(nFlags);
  233. if(!ih->IsBypassed() && ih->KeyEvent(kCtxViewComments, nChar, nRepCnt, nFlags, kT) != kcNull)
  234. {
  235. return TRUE; // Mapped to a command, no need to pass message on.
  236. }
  237. }
  238. }
  239. return CModScrollView::PreTranslateMessage(pMsg);
  240. }
  241. ///////////////////////////////////////////////////////////////
  242. // CViewComments drawing
  243. void CViewComments::UpdateView(UpdateHint hint, CObject *)
  244. {
  245. const CModDoc *pModDoc = GetDocument();
  246. if ((!pModDoc) || (!(m_ItemList.m_hWnd))) return;
  247. const FlagSet<HintType> hintType = hint.GetType();
  248. if (hintType[HINT_MPTOPTIONS])
  249. {
  250. m_ToolBar.UpdateStyle();
  251. }
  252. const SampleHint sampleHint = hint.ToType<SampleHint>();
  253. const InstrumentHint instrHint = hint.ToType<InstrumentHint>();
  254. const bool updateSamples = sampleHint.GetType()[HINT_SMPNAMES | HINT_SAMPLEINFO];
  255. const bool updateInstr = instrHint.GetType()[HINT_INSNAMES|HINT_INSTRUMENT];
  256. bool updateAll = hintType[HINT_MODTYPE];
  257. if(!updateSamples && !updateInstr && !updateAll) return;
  258. const CSoundFile &sndFile = pModDoc->GetSoundFile();
  259. m_ToolBar.ChangeBitmap(IDC_LIST_INSTRUMENTS, sndFile.GetNumInstruments() ? IMAGE_INSTRUMENTS : IMAGE_INSTRMUTE);
  260. CString s;
  261. LV_ITEM lvi, lvi2;
  262. m_ItemList.SetRedraw(FALSE);
  263. // Add sample headers
  264. if (m_nListId != m_nCurrentListId || updateAll)
  265. {
  266. UINT ichk = 0;
  267. m_ItemList.DeleteAllItems();
  268. while ((m_ItemList.DeleteColumn(0)) && (ichk < 25)) ichk++;
  269. m_nCurrentListId = m_nListId;
  270. if (m_nCurrentListId == IDC_LIST_SAMPLES)
  271. {
  272. // Add Sample Headers
  273. m_ItemList.SetHeaders(gSampleHeaders);
  274. } else if (m_nCurrentListId == IDC_LIST_INSTRUMENTS)
  275. {
  276. // Add Instrument Headers
  277. m_ItemList.SetHeaders(gInstrumentHeaders);
  278. } else
  279. updateAll = true;
  280. }
  281. // Add Items
  282. UINT nCount = m_ItemList.GetItemCount();
  283. // Add Samples
  284. if (m_nCurrentListId == IDC_LIST_SAMPLES && (updateAll || updateSamples))
  285. {
  286. SAMPLEINDEX nMax = static_cast<SAMPLEINDEX>(nCount);
  287. if (nMax < sndFile.GetNumSamples()) nMax = sndFile.GetNumSamples();
  288. for (SAMPLEINDEX iSmp = 0; iSmp < nMax; iSmp++)
  289. {
  290. if (iSmp < sndFile.GetNumSamples())
  291. {
  292. UINT nCol = 0;
  293. for (UINT iCol=0; iCol<SMPLIST_COLUMNS; iCol++)
  294. {
  295. const ModSample &sample = sndFile.GetSample(iSmp + 1);
  296. s.Empty();
  297. switch(iCol)
  298. {
  299. case SMPLIST_SAMPLENAME:
  300. s = mpt::ToCString(sndFile.GetCharsetInternal(), sndFile.m_szNames[iSmp + 1]);
  301. break;
  302. case SMPLIST_SAMPLENO:
  303. s = mpt::cfmt::dec0<2>(iSmp + 1);
  304. break;
  305. case SMPLIST_SIZE:
  306. if(sample.nLength && !sample.uFlags[CHN_ADLIB])
  307. {
  308. auto size = sample.GetSampleSizeInBytes();
  309. if(size >= 1024)
  310. s.Format(_T("%u KB"), size >> 10);
  311. else
  312. s.Format(_T("%u B"), size);
  313. }
  314. break;
  315. case SMPLIST_TYPE:
  316. if(sample.uFlags[CHN_ADLIB])
  317. s = _T("OPL");
  318. else if(sample.HasSampleData())
  319. s = MPT_CFORMAT("{} Bit")(sample.GetElementarySampleSize() * 8);
  320. break;
  321. case SMPLIST_INSTR:
  322. if (sndFile.GetNumInstruments())
  323. {
  324. bool first = true;
  325. for (INSTRUMENTINDEX i = 1; i <= sndFile.GetNumInstruments(); i++)
  326. {
  327. if (sndFile.IsSampleReferencedByInstrument(iSmp + 1, i))
  328. {
  329. if (!first) s.AppendChar(_T(','));
  330. first = false;
  331. s.AppendFormat(_T("%u"), i);
  332. }
  333. }
  334. }
  335. break;
  336. case SMPLIST_MIDDLEC:
  337. if (sample.nLength)
  338. {
  339. s.Format(_T("%u Hz"), sample.GetSampleRate(sndFile.GetType()));
  340. }
  341. break;
  342. case SMPLIST_FILENAME:
  343. s = mpt::ToCString(sndFile.GetCharsetInternal(), sample.filename);
  344. break;
  345. case SMPLIST_PATH:
  346. s = sndFile.GetSamplePath(iSmp + 1).ToCString();
  347. break;
  348. }
  349. lvi.mask = LVIF_TEXT;
  350. lvi.iItem = iSmp;
  351. lvi.iSubItem = nCol;
  352. lvi.pszText = const_cast<TCHAR *>(s.GetString());
  353. if ((iCol) || (iSmp < nCount))
  354. {
  355. bool update = true;
  356. if (iSmp < nCount)
  357. {
  358. TCHAR stmp[512];
  359. lvi2 = lvi;
  360. lvi2.pszText = stmp;
  361. lvi2.cchTextMax = mpt::saturate_cast<int>(std::size(stmp));
  362. stmp[0] = 0;
  363. m_ItemList.GetItem(&lvi2);
  364. if (s == stmp) update = false;
  365. }
  366. if (update) m_ItemList.SetItem(&lvi);
  367. } else
  368. {
  369. m_ItemList.InsertItem(&lvi);
  370. }
  371. nCol++;
  372. }
  373. } else
  374. {
  375. m_ItemList.DeleteItem(iSmp);
  376. }
  377. }
  378. } else
  379. // Add Instruments
  380. if ((m_nCurrentListId == IDC_LIST_INSTRUMENTS) && (updateAll || updateInstr))
  381. {
  382. INSTRUMENTINDEX nMax = static_cast<INSTRUMENTINDEX>(nCount);
  383. if (nMax < sndFile.GetNumInstruments()) nMax = sndFile.GetNumInstruments();
  384. for (INSTRUMENTINDEX iIns = 0; iIns < nMax; iIns++)
  385. {
  386. if (iIns < sndFile.GetNumInstruments())
  387. {
  388. UINT nCol = 0;
  389. for (UINT iCol=0; iCol<INSLIST_COLUMNS; iCol++)
  390. {
  391. ModInstrument *pIns = sndFile.Instruments[iIns+1];
  392. s.Empty();
  393. switch(iCol)
  394. {
  395. case INSLIST_INSTRUMENTNAME:
  396. if (pIns) s = mpt::ToCString(sndFile.GetCharsetInternal(), pIns->name);
  397. break;
  398. case INSLIST_INSTRUMENTNO:
  399. s = mpt::cfmt::dec0<2>(iIns + 1);
  400. break;
  401. case INSLIST_SAMPLES:
  402. if (pIns)
  403. {
  404. bool first = true;
  405. for(auto sample : pIns->GetSamples())
  406. {
  407. if(!first) s.AppendChar(_T(','));
  408. first = false;
  409. s.AppendFormat(_T("%u"), sample);
  410. }
  411. }
  412. break;
  413. case INSLIST_ENVELOPES:
  414. if (pIns)
  415. {
  416. if (pIns->VolEnv.dwFlags[ENV_ENABLED]) s += _T("Vol");
  417. if (pIns->PanEnv.dwFlags[ENV_ENABLED]) { if (!s.IsEmpty()) s += _T(", "); s += _T("Pan"); }
  418. if (pIns->PitchEnv.dwFlags[ENV_ENABLED]) { if (!s.IsEmpty()) s += _T(", "); s += (pIns->PitchEnv.dwFlags[ENV_FILTER] ? _T("Filter") : _T("Pitch")); }
  419. }
  420. break;
  421. case INSLIST_FILENAME:
  422. if (pIns)
  423. {
  424. s = mpt::ToCString(sndFile.GetCharsetInternal(), pIns->filename);
  425. }
  426. break;
  427. case INSLIST_PLUGIN:
  428. if (pIns != nullptr && pIns->nMixPlug > 0 && sndFile.m_MixPlugins[pIns->nMixPlug - 1].IsValidPlugin())
  429. {
  430. s.Format(_T("FX%02u: "), pIns->nMixPlug);
  431. s += mpt::ToCString(sndFile.m_MixPlugins[pIns->nMixPlug - 1].GetLibraryName());
  432. }
  433. break;
  434. }
  435. lvi.mask = LVIF_TEXT;
  436. lvi.iItem = iIns;
  437. lvi.iSubItem = nCol;
  438. lvi.pszText = const_cast<TCHAR *>(s.GetString());
  439. if ((iCol) || (iIns < nCount))
  440. {
  441. bool update = true;
  442. if (iIns < nCount)
  443. {
  444. TCHAR stmp[512];
  445. lvi2 = lvi;
  446. lvi2.pszText = stmp;
  447. lvi2.cchTextMax = mpt::saturate_cast<int>(std::size(stmp));
  448. stmp[0] = 0;
  449. m_ItemList.GetItem(&lvi2);
  450. if (s == stmp) update = false;
  451. }
  452. if (update) m_ItemList.SetItem(&lvi);
  453. } else
  454. {
  455. m_ItemList.InsertItem(&lvi);
  456. }
  457. nCol++;
  458. }
  459. } else
  460. {
  461. m_ItemList.DeleteItem(iIns);
  462. }
  463. }
  464. } else
  465. // Add Patterns
  466. //if ((m_nCurrentListId == IDC_LIST_PATTERNS) && (hintType & (HINT_MODTYPE|HINT_PATNAMES|HINT_PATTERNROW)))
  467. {
  468. }
  469. m_ItemList.SetRedraw(TRUE);
  470. }
  471. void CViewComments::RecalcLayout()
  472. {
  473. CRect rect;
  474. if (!m_hWnd) return;
  475. GetClientRect(&rect);
  476. m_ToolBar.SetWindowPos(NULL, 0, 0, rect.Width(), DETAILS_TOOLBAR_CY, SWP_NOZORDER|SWP_NOACTIVATE);
  477. m_ItemList.SetWindowPos(NULL, -1, DETAILS_TOOLBAR_CY, rect.Width()+2, rect.Height() - DETAILS_TOOLBAR_CY + 1, SWP_NOZORDER|SWP_NOACTIVATE);
  478. }
  479. void CViewComments::UpdateButtonState()
  480. {
  481. const CModDoc *pModDoc = GetDocument();
  482. if (pModDoc)
  483. {
  484. m_ToolBar.SetState(IDC_LIST_SAMPLES, ((m_nListId == IDC_LIST_SAMPLES) ? TBSTATE_CHECKED : 0)|TBSTATE_ENABLED);
  485. m_ToolBar.SetState(IDC_LIST_INSTRUMENTS, ((m_nListId == IDC_LIST_INSTRUMENTS) ? TBSTATE_CHECKED : 0)|TBSTATE_ENABLED);
  486. m_ToolBar.SetState(IDC_LIST_PATTERNS, ((m_nListId == IDC_LIST_PATTERNS) ? TBSTATE_CHECKED : 0)|TBSTATE_ENABLED);
  487. m_ToolBar.EnableButton(IDC_LIST_INSTRUMENTS, (pModDoc->GetNumInstruments()) ? TRUE : FALSE);
  488. }
  489. }
  490. void CViewComments::OnBeginLabelEdit(LPNMHDR, LRESULT *)
  491. {
  492. CEdit *editCtrl = m_ItemList.GetEditControl();
  493. if(editCtrl)
  494. {
  495. const CModSpecifications &specs = GetDocument()->GetSoundFile().GetModSpecifications();
  496. const auto maxStrLen = (m_nListId == IDC_LIST_SAMPLES) ? specs.sampleNameLengthMax : specs.instrNameLengthMax;
  497. editCtrl->LimitText(maxStrLen);
  498. CMainFrame::GetInputHandler()->Bypass(true);
  499. }
  500. }
  501. void CViewComments::OnEndLabelEdit(LPNMHDR pnmhdr, LRESULT *)
  502. {
  503. CMainFrame::GetInputHandler()->Bypass(false);
  504. LV_DISPINFO *plvDispInfo = (LV_DISPINFO *)pnmhdr;
  505. LV_ITEM &lvItem = plvDispInfo->item;
  506. CModDoc *pModDoc = GetDocument();
  507. if(lvItem.pszText != nullptr && !lvItem.iSubItem && pModDoc)
  508. {
  509. UINT iItem = lvItem.iItem;
  510. CSoundFile &sndFile = pModDoc->GetSoundFile();
  511. if(m_nListId == IDC_LIST_SAMPLES)
  512. {
  513. if(iItem < sndFile.GetNumSamples())
  514. {
  515. sndFile.m_szNames[iItem + 1] = mpt::ToCharset(sndFile.GetCharsetInternal(), CString(lvItem.pszText));
  516. pModDoc->UpdateAllViews(this, SampleHint(static_cast<SAMPLEINDEX>(iItem + 1)).Info().Names(), this);
  517. pModDoc->SetModified();
  518. }
  519. } else if(m_nListId == IDC_LIST_INSTRUMENTS)
  520. {
  521. if((iItem < sndFile.GetNumInstruments()) && (sndFile.Instruments[iItem + 1]))
  522. {
  523. ModInstrument *pIns = sndFile.Instruments[iItem + 1];
  524. pIns->name = mpt::ToCharset(sndFile.GetCharsetInternal(), CString(lvItem.pszText));
  525. pModDoc->UpdateAllViews(this, InstrumentHint(static_cast<INSTRUMENTINDEX>(iItem + 1)).Info().Names(), this);
  526. pModDoc->SetModified();
  527. }
  528. } else
  529. {
  530. return;
  531. }
  532. m_ItemList.SetItemText(iItem, lvItem.iSubItem, lvItem.pszText);
  533. }
  534. }
  535. ///////////////////////////////////////////////////////////////
  536. // CViewComments messages
  537. void CViewComments::OnSize(UINT nType, int cx, int cy)
  538. {
  539. CModScrollView::OnSize(nType, cx, cy);
  540. if (((nType == SIZE_RESTORED) || (nType == SIZE_MAXIMIZED)) && (cx > 0) && (cy > 0) && (m_hWnd))
  541. {
  542. RecalcLayout();
  543. }
  544. }
  545. bool CViewComments::SwitchToList(int list)
  546. {
  547. if(list == m_nListId)
  548. return false;
  549. if(list == IDC_LIST_SAMPLES)
  550. {
  551. m_nListId = IDC_LIST_SAMPLES;
  552. UpdateButtonState();
  553. UpdateView(UpdateHint().ModType());
  554. } else if(list == IDC_LIST_INSTRUMENTS)
  555. {
  556. const CModDoc *modDoc = GetDocument();
  557. if(!modDoc || !modDoc->GetNumInstruments())
  558. return false;
  559. m_nListId = IDC_LIST_INSTRUMENTS;
  560. UpdateButtonState();
  561. UpdateView(UpdateHint().ModType());
  562. /*} else if(list == IDC_LIST_PATTERNS)
  563. {
  564. m_nListId = IDC_LIST_PATTERNS;
  565. UpdateButtonState();
  566. UpdateView(UpdateHint().ModType());*/
  567. } else
  568. {
  569. return false;
  570. }
  571. return true;
  572. }
  573. void CViewComments::OnDblClickListItem(NMHDR *, LRESULT *)
  574. {
  575. // Double click -> switch to instrument or sample tab
  576. int nItem = m_ItemList.GetSelectionMark();
  577. if(nItem == -1) return;
  578. CModDoc *pModDoc = GetDocument();
  579. if(!pModDoc) return;
  580. nItem++;
  581. switch(m_nListId)
  582. {
  583. case IDC_LIST_SAMPLES:
  584. pModDoc->ViewSample(nItem);
  585. break;
  586. case IDC_LIST_INSTRUMENTS:
  587. pModDoc->ViewInstrument(nItem);
  588. break;
  589. case IDC_LIST_PATTERNS:
  590. pModDoc->ViewPattern(nItem, 0);
  591. break;
  592. }
  593. }
  594. void CViewComments::OnRClickListItem(NMHDR *, LRESULT *)
  595. {
  596. HMENU menu = ::CreatePopupMenu();
  597. ::AppendMenu(menu, MF_STRING, ID_COPY_ALL_NAMES, _T("&Copy Names"));
  598. CPoint pt;
  599. ::GetCursorPos(&pt);
  600. ::TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, m_hWnd, NULL);
  601. ::DestroyMenu(menu);
  602. }
  603. void CViewComments::OnCopyNames()
  604. {
  605. std::wstring names;
  606. const CSoundFile &sndFile = GetDocument()->GetSoundFile();
  607. if(m_nListId == IDC_LIST_SAMPLES)
  608. {
  609. for(SAMPLEINDEX i = 1; i <= sndFile.GetNumSamples(); i++)
  610. names += mpt::ToWide(sndFile.GetCharsetInternal(), sndFile.GetSampleName(i)) + L"\r\n";
  611. } else if(m_nListId == IDC_LIST_INSTRUMENTS)
  612. {
  613. for(INSTRUMENTINDEX i = 1; i <= sndFile.GetNumInstruments(); i++)
  614. names += mpt::ToWide(sndFile.GetCharsetInternal(), sndFile.GetInstrumentName(i)) + L"\r\n";
  615. }
  616. const size_t sizeBytes = (names.length() + 1) * sizeof(wchar_t);
  617. Clipboard clipboard(CF_UNICODETEXT, sizeBytes);
  618. if(auto dst = clipboard.Get(); dst.data())
  619. {
  620. std::memcpy(dst.data(), names.c_str(), sizeBytes);
  621. }
  622. }
  623. OPENMPT_NAMESPACE_END