ExternalSamples.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /*
  2. * ExternalSamples.cpp
  3. * -------------------
  4. * Purpose: Dialogs for locating missing external samples and handling modified samples
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include "Moddoc.h"
  11. #include "ExternalSamples.h"
  12. #include "FileDialog.h"
  13. #include "FolderScanner.h"
  14. #include "TrackerSettings.h"
  15. #include "Reporting.h"
  16. #include "resource.h"
  17. OPENMPT_NAMESPACE_BEGIN
  18. BEGIN_MESSAGE_MAP(MissingExternalSamplesDlg, ResizableDialog)
  19. //{{AFX_MSG_MAP(ExternalSamplesDlg)
  20. ON_NOTIFY(NM_DBLCLK, IDC_LIST1, &MissingExternalSamplesDlg::OnSetPath)
  21. ON_COMMAND(IDC_BUTTON1, &MissingExternalSamplesDlg::OnScanFolder)
  22. //}}AFX_MSG_MAP
  23. END_MESSAGE_MAP()
  24. void MissingExternalSamplesDlg::DoDataExchange(CDataExchange *pDX)
  25. {
  26. ResizableDialog::DoDataExchange(pDX);
  27. DDX_Control(pDX, IDC_LIST1, m_List);
  28. }
  29. MissingExternalSamplesDlg::MissingExternalSamplesDlg(CModDoc &modDoc, CWnd *parent)
  30. : ResizableDialog(IDD_MISSINGSAMPLES, parent)
  31. , m_modDoc(modDoc)
  32. , m_sndFile(modDoc.GetSoundFile())
  33. {
  34. }
  35. BOOL MissingExternalSamplesDlg::OnInitDialog()
  36. {
  37. ResizableDialog::OnInitDialog();
  38. // Initialize table
  39. const CListCtrlEx::Header headers[] =
  40. {
  41. { _T("Sample"), 128, LVCFMT_LEFT },
  42. { _T("External Filename"), 308, LVCFMT_LEFT },
  43. };
  44. m_List.SetHeaders(headers);
  45. m_List.SetExtendedStyle(m_List.GetExtendedStyle() | LVS_EX_FULLROWSELECT);
  46. GenerateList();
  47. SetWindowText((_T("Missing External Samples - ") + m_modDoc.GetPathNameMpt().GetFullFileName().AsNative()).c_str());
  48. return TRUE;
  49. }
  50. void MissingExternalSamplesDlg::GenerateList()
  51. {
  52. m_List.SetRedraw(FALSE);
  53. m_List.DeleteAllItems();
  54. CString s;
  55. for(SAMPLEINDEX smp = 1; smp <= m_sndFile.GetNumSamples(); smp++)
  56. {
  57. if(m_sndFile.IsExternalSampleMissing(smp))
  58. {
  59. s.Format(_T("%02u: "), smp);
  60. s += mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.GetSampleName(smp));
  61. int insertAt = m_List.InsertItem(m_List.GetItemCount(), s);
  62. if(insertAt == -1)
  63. continue;
  64. m_List.SetItemText(insertAt, 1, m_sndFile.GetSamplePath(smp).AsNative().c_str());
  65. m_List.SetItemData(insertAt, smp);
  66. }
  67. }
  68. m_List.SetRedraw(TRUE);
  69. // Yay, we managed to find all samples!
  70. if(!m_List.GetItemCount())
  71. OnOK();
  72. }
  73. void MissingExternalSamplesDlg::OnSetPath(NMHDR *, LRESULT *)
  74. {
  75. const int item = m_List.GetSelectionMark();
  76. if(item == -1) return;
  77. const SAMPLEINDEX smp = static_cast<SAMPLEINDEX>(m_List.GetItemData(item));
  78. const mpt::PathString path = m_modDoc.GetSoundFile().GetSamplePath(smp);
  79. FileDialog dlg = OpenFileDialog()
  80. .ExtensionFilter("All Samples|*.wav;*.flac|All files(*.*)|*.*||"); // Only show samples that we actually can save as well.
  81. if(TrackerSettings::Instance().previewInFileDialogs)
  82. dlg.EnableAudioPreview();
  83. if(path.empty())
  84. dlg.WorkingDirectory(TrackerSettings::Instance().PathSamples.GetWorkingDir());
  85. else
  86. dlg.DefaultFilename(path);
  87. if(!dlg.Show()) return;
  88. TrackerSettings::Instance().PathSamples.SetWorkingDir(dlg.GetWorkingDirectory());
  89. SetSample(smp, dlg.GetFirstFile());
  90. m_modDoc.UpdateAllViews(nullptr, SampleHint(smp).Info().Names().Data());
  91. GenerateList();
  92. }
  93. void MissingExternalSamplesDlg::OnScanFolder()
  94. {
  95. if(m_isScanning)
  96. {
  97. m_isScanning = false;
  98. return;
  99. }
  100. BrowseForFolder dlg(TrackerSettings::Instance().PathSamples.GetWorkingDir(), _T("Select a folder to search for missing samples..."));
  101. if(dlg.Show())
  102. {
  103. TrackerSettings::Instance().PathSamples.SetWorkingDir(dlg.GetDirectory());
  104. FolderScanner scan(dlg.GetDirectory(), FolderScanner::kOnlyFiles | FolderScanner::kFindInSubDirectories);
  105. mpt::PathString fileName;
  106. m_isScanning = true;
  107. SetDlgItemText(IDC_BUTTON1, _T("&Cancel"));
  108. GetDlgItem(IDOK)->EnableWindow(FALSE);
  109. BeginWaitCursor();
  110. DWORD64 lastTick = Util::GetTickCount64();
  111. int foundFiles = 0;
  112. bool anyMissing = true;
  113. while(scan.Next(fileName) && m_isScanning && anyMissing)
  114. {
  115. anyMissing = false;
  116. for(SAMPLEINDEX smp = 1; smp <= m_sndFile.GetNumSamples(); smp++)
  117. {
  118. if(m_sndFile.IsExternalSampleMissing(smp))
  119. {
  120. if(!mpt::PathString::CompareNoCase(m_sndFile.GetSamplePath(smp).GetFullFileName(), fileName.GetFullFileName()))
  121. {
  122. if(SetSample(smp, fileName))
  123. {
  124. foundFiles++;
  125. }
  126. } else
  127. {
  128. anyMissing = true;
  129. }
  130. }
  131. }
  132. const DWORD64 tick = Util::GetTickCount64();
  133. if(tick < lastTick || tick > lastTick + 100)
  134. {
  135. lastTick = tick;
  136. SetDlgItemText(IDC_STATIC1, fileName.AsNative().c_str());
  137. MSG msg;
  138. while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  139. {
  140. ::TranslateMessage(&msg);
  141. ::DispatchMessage(&msg);
  142. }
  143. }
  144. }
  145. EndWaitCursor();
  146. GetDlgItem(IDOK)->EnableWindow(TRUE);
  147. SetDlgItemText(IDC_BUTTON1, _T("&Scan Folder..."));
  148. m_modDoc.UpdateAllViews(nullptr, SampleHint().Info().Data().Names());
  149. if(foundFiles)
  150. {
  151. SetDlgItemText(IDC_STATIC1, MPT_CFORMAT("{} sample paths were relocated.")(foundFiles));
  152. } else
  153. {
  154. SetDlgItemText(IDC_STATIC1, _T("No matching sample names found."));
  155. }
  156. m_isScanning = false;
  157. GenerateList();
  158. }
  159. }
  160. bool MissingExternalSamplesDlg::SetSample(SAMPLEINDEX smp, const mpt::PathString &fileName)
  161. {
  162. m_modDoc.GetSampleUndo().PrepareUndo(smp, sundo_replace, "Replace");
  163. const mpt::PathString oldPath = m_sndFile.GetSamplePath(smp);
  164. if(!m_sndFile.LoadExternalSample(smp, fileName))
  165. {
  166. Reporting::Information(_T("Unable to load sample:\n") + fileName.AsNative());
  167. m_modDoc.GetSampleUndo().RemoveLastUndoStep(smp);
  168. return false;
  169. } else
  170. {
  171. // Maybe we just put the file into its regular place, in which case the module has not really been modified.
  172. if(oldPath != fileName)
  173. {
  174. m_modDoc.SetModified();
  175. }
  176. return true;
  177. }
  178. }
  179. BEGIN_MESSAGE_MAP(ModifiedExternalSamplesDlg, ResizableDialog)
  180. //{{AFX_MSG_MAP(ExternalSamplesDlg)
  181. ON_COMMAND(IDC_SAVE, &ModifiedExternalSamplesDlg::OnSaveSelected)
  182. ON_COMMAND(IDC_CHECK1, &ModifiedExternalSamplesDlg::OnCheckAll)
  183. ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &ModifiedExternalSamplesDlg::OnSelectionChanged)
  184. //}}AFX_MSG_MAP
  185. END_MESSAGE_MAP()
  186. void ModifiedExternalSamplesDlg::DoDataExchange(CDataExchange *pDX)
  187. {
  188. ResizableDialog::DoDataExchange(pDX);
  189. DDX_Control(pDX, IDC_LIST1, m_List);
  190. }
  191. ModifiedExternalSamplesDlg::ModifiedExternalSamplesDlg(CModDoc &modDoc, CWnd *parent)
  192. : ResizableDialog(IDD_MODIFIEDSAMPLES, parent)
  193. , m_modDoc(modDoc)
  194. , m_sndFile(modDoc.GetSoundFile())
  195. {
  196. }
  197. BOOL ModifiedExternalSamplesDlg::OnInitDialog()
  198. {
  199. ResizableDialog::OnInitDialog();
  200. // Initialize table
  201. const CListCtrlEx::Header headers[] =
  202. {
  203. {_T("Sample"), 120, LVCFMT_LEFT},
  204. {_T("Status"), 54, LVCFMT_LEFT},
  205. {_T("External Filename"), 262, LVCFMT_LEFT},
  206. };
  207. m_List.SetHeaders(headers);
  208. m_List.SetExtendedStyle(m_List.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
  209. GenerateList();
  210. SetWindowText((_T("Modified External Samples - ") + m_modDoc.GetPathNameMpt().GetFullFileName().AsNative()).c_str());
  211. return TRUE;
  212. }
  213. void ModifiedExternalSamplesDlg::GenerateList()
  214. {
  215. m_List.SetRedraw(FALSE);
  216. m_List.DeleteAllItems();
  217. CString s;
  218. mpt::winstring status;
  219. for(SAMPLEINDEX smp = 1; smp <= m_sndFile.GetNumSamples(); smp++)
  220. {
  221. if(!m_sndFile.GetSample(smp).uFlags[SMP_KEEPONDISK])
  222. continue;
  223. if(m_sndFile.GetSample(smp).uFlags[SMP_MODIFIED])
  224. status = _T("modified");
  225. else if(!m_sndFile.GetSamplePath(smp).IsFile())
  226. status = _T("missing");
  227. else
  228. continue;
  229. s.Format(_T("%02u: "), smp);
  230. s += mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.GetSampleName(smp));
  231. int insertAt = m_List.InsertItem(m_List.GetItemCount(), s);
  232. if(insertAt == -1)
  233. continue;
  234. m_List.SetItemText(insertAt, 1, status.c_str());
  235. m_List.SetItemText(insertAt, 2, m_sndFile.GetSamplePath(smp).AsNative().c_str());
  236. m_List.SetCheck(insertAt, TRUE);
  237. m_List.SetItemData(insertAt, smp);
  238. }
  239. m_List.SetRedraw(TRUE);
  240. CheckDlgButton(IDC_CHECK1, BST_CHECKED);
  241. OnSelectionChanged(nullptr, nullptr);
  242. // Nothing modified?
  243. if(!m_List.GetItemCount())
  244. OnOK();
  245. }
  246. void ModifiedExternalSamplesDlg::OnCheckAll()
  247. {
  248. const BOOL check = IsDlgButtonChecked(IDC_CHECK1) ? TRUE : FALSE;
  249. const int count = m_List.GetItemCount();
  250. for(int i = 0; i < count; i++)
  251. {
  252. m_List.SetCheck(i, check);
  253. }
  254. }
  255. void ModifiedExternalSamplesDlg::OnSelectionChanged(NMHDR *, LRESULT *)
  256. {
  257. int numChecked = 0;
  258. const int count = m_List.GetItemCount();
  259. for(int i = 0; i < count; i++)
  260. {
  261. if(m_List.GetCheck(i))
  262. numChecked++;
  263. }
  264. const TCHAR *embedText, *saveText;
  265. if(numChecked == count)
  266. {
  267. embedText = _T("&Embed All");
  268. saveText = _T("&Save All");
  269. } else if(!numChecked)
  270. {
  271. embedText = _T("&Embed None");
  272. saveText = _T("&Save None");
  273. } else
  274. {
  275. embedText = _T("&Embed Selected");
  276. saveText = _T("&Save Selected");
  277. }
  278. GetDlgItem(IDOK)->SetWindowText(embedText);
  279. GetDlgItem(IDC_SAVE)->SetWindowText(saveText);
  280. }
  281. void ModifiedExternalSamplesDlg::Execute(bool doSave)
  282. {
  283. ScopedLogCapturer log(m_modDoc, _T("Modified Samples"), this);
  284. bool ok = true;
  285. const int count = m_List.GetItemCount();
  286. for(int i = 0; i < count; i++)
  287. {
  288. if(!m_List.GetCheck(i))
  289. continue;
  290. SAMPLEINDEX smp = static_cast<SAMPLEINDEX>(m_List.GetItemData(i));
  291. if(doSave)
  292. {
  293. ok &= m_modDoc.SaveSample(smp);
  294. } else
  295. {
  296. m_sndFile.GetSample(smp).uFlags.reset(SMP_KEEPONDISK);
  297. m_modDoc.SetModified();
  298. }
  299. }
  300. if(ok)
  301. ResizableDialog::OnOK();
  302. else
  303. ResizableDialog::OnCancel();
  304. }
  305. OPENMPT_NAMESPACE_END