/* * ExternalSamples.cpp * ------------------- * Purpose: Dialogs for locating missing external samples and handling modified samples * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Moddoc.h" #include "ExternalSamples.h" #include "FileDialog.h" #include "FolderScanner.h" #include "TrackerSettings.h" #include "Reporting.h" #include "resource.h" OPENMPT_NAMESPACE_BEGIN BEGIN_MESSAGE_MAP(MissingExternalSamplesDlg, ResizableDialog) //{{AFX_MSG_MAP(ExternalSamplesDlg) ON_NOTIFY(NM_DBLCLK, IDC_LIST1, &MissingExternalSamplesDlg::OnSetPath) ON_COMMAND(IDC_BUTTON1, &MissingExternalSamplesDlg::OnScanFolder) //}}AFX_MSG_MAP END_MESSAGE_MAP() void MissingExternalSamplesDlg::DoDataExchange(CDataExchange *pDX) { ResizableDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST1, m_List); } MissingExternalSamplesDlg::MissingExternalSamplesDlg(CModDoc &modDoc, CWnd *parent) : ResizableDialog(IDD_MISSINGSAMPLES, parent) , m_modDoc(modDoc) , m_sndFile(modDoc.GetSoundFile()) { } BOOL MissingExternalSamplesDlg::OnInitDialog() { ResizableDialog::OnInitDialog(); // Initialize table const CListCtrlEx::Header headers[] = { { _T("Sample"), 128, LVCFMT_LEFT }, { _T("External Filename"), 308, LVCFMT_LEFT }, }; m_List.SetHeaders(headers); m_List.SetExtendedStyle(m_List.GetExtendedStyle() | LVS_EX_FULLROWSELECT); GenerateList(); SetWindowText((_T("Missing External Samples - ") + m_modDoc.GetPathNameMpt().GetFullFileName().AsNative()).c_str()); return TRUE; } void MissingExternalSamplesDlg::GenerateList() { m_List.SetRedraw(FALSE); m_List.DeleteAllItems(); CString s; for(SAMPLEINDEX smp = 1; smp <= m_sndFile.GetNumSamples(); smp++) { if(m_sndFile.IsExternalSampleMissing(smp)) { s.Format(_T("%02u: "), smp); s += mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.GetSampleName(smp)); int insertAt = m_List.InsertItem(m_List.GetItemCount(), s); if(insertAt == -1) continue; m_List.SetItemText(insertAt, 1, m_sndFile.GetSamplePath(smp).AsNative().c_str()); m_List.SetItemData(insertAt, smp); } } m_List.SetRedraw(TRUE); // Yay, we managed to find all samples! if(!m_List.GetItemCount()) OnOK(); } void MissingExternalSamplesDlg::OnSetPath(NMHDR *, LRESULT *) { const int item = m_List.GetSelectionMark(); if(item == -1) return; const SAMPLEINDEX smp = static_cast(m_List.GetItemData(item)); const mpt::PathString path = m_modDoc.GetSoundFile().GetSamplePath(smp); FileDialog dlg = OpenFileDialog() .ExtensionFilter("All Samples|*.wav;*.flac|All files(*.*)|*.*||"); // Only show samples that we actually can save as well. if(TrackerSettings::Instance().previewInFileDialogs) dlg.EnableAudioPreview(); if(path.empty()) dlg.WorkingDirectory(TrackerSettings::Instance().PathSamples.GetWorkingDir()); else dlg.DefaultFilename(path); if(!dlg.Show()) return; TrackerSettings::Instance().PathSamples.SetWorkingDir(dlg.GetWorkingDirectory()); SetSample(smp, dlg.GetFirstFile()); m_modDoc.UpdateAllViews(nullptr, SampleHint(smp).Info().Names().Data()); GenerateList(); } void MissingExternalSamplesDlg::OnScanFolder() { if(m_isScanning) { m_isScanning = false; return; } BrowseForFolder dlg(TrackerSettings::Instance().PathSamples.GetWorkingDir(), _T("Select a folder to search for missing samples...")); if(dlg.Show()) { TrackerSettings::Instance().PathSamples.SetWorkingDir(dlg.GetDirectory()); FolderScanner scan(dlg.GetDirectory(), FolderScanner::kOnlyFiles | FolderScanner::kFindInSubDirectories); mpt::PathString fileName; m_isScanning = true; SetDlgItemText(IDC_BUTTON1, _T("&Cancel")); GetDlgItem(IDOK)->EnableWindow(FALSE); BeginWaitCursor(); DWORD64 lastTick = Util::GetTickCount64(); int foundFiles = 0; bool anyMissing = true; while(scan.Next(fileName) && m_isScanning && anyMissing) { anyMissing = false; for(SAMPLEINDEX smp = 1; smp <= m_sndFile.GetNumSamples(); smp++) { if(m_sndFile.IsExternalSampleMissing(smp)) { if(!mpt::PathString::CompareNoCase(m_sndFile.GetSamplePath(smp).GetFullFileName(), fileName.GetFullFileName())) { if(SetSample(smp, fileName)) { foundFiles++; } } else { anyMissing = true; } } } const DWORD64 tick = Util::GetTickCount64(); if(tick < lastTick || tick > lastTick + 100) { lastTick = tick; SetDlgItemText(IDC_STATIC1, fileName.AsNative().c_str()); MSG msg; while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } } EndWaitCursor(); GetDlgItem(IDOK)->EnableWindow(TRUE); SetDlgItemText(IDC_BUTTON1, _T("&Scan Folder...")); m_modDoc.UpdateAllViews(nullptr, SampleHint().Info().Data().Names()); if(foundFiles) { SetDlgItemText(IDC_STATIC1, MPT_CFORMAT("{} sample paths were relocated.")(foundFiles)); } else { SetDlgItemText(IDC_STATIC1, _T("No matching sample names found.")); } m_isScanning = false; GenerateList(); } } bool MissingExternalSamplesDlg::SetSample(SAMPLEINDEX smp, const mpt::PathString &fileName) { m_modDoc.GetSampleUndo().PrepareUndo(smp, sundo_replace, "Replace"); const mpt::PathString oldPath = m_sndFile.GetSamplePath(smp); if(!m_sndFile.LoadExternalSample(smp, fileName)) { Reporting::Information(_T("Unable to load sample:\n") + fileName.AsNative()); m_modDoc.GetSampleUndo().RemoveLastUndoStep(smp); return false; } else { // Maybe we just put the file into its regular place, in which case the module has not really been modified. if(oldPath != fileName) { m_modDoc.SetModified(); } return true; } } BEGIN_MESSAGE_MAP(ModifiedExternalSamplesDlg, ResizableDialog) //{{AFX_MSG_MAP(ExternalSamplesDlg) ON_COMMAND(IDC_SAVE, &ModifiedExternalSamplesDlg::OnSaveSelected) ON_COMMAND(IDC_CHECK1, &ModifiedExternalSamplesDlg::OnCheckAll) ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &ModifiedExternalSamplesDlg::OnSelectionChanged) //}}AFX_MSG_MAP END_MESSAGE_MAP() void ModifiedExternalSamplesDlg::DoDataExchange(CDataExchange *pDX) { ResizableDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST1, m_List); } ModifiedExternalSamplesDlg::ModifiedExternalSamplesDlg(CModDoc &modDoc, CWnd *parent) : ResizableDialog(IDD_MODIFIEDSAMPLES, parent) , m_modDoc(modDoc) , m_sndFile(modDoc.GetSoundFile()) { } BOOL ModifiedExternalSamplesDlg::OnInitDialog() { ResizableDialog::OnInitDialog(); // Initialize table const CListCtrlEx::Header headers[] = { {_T("Sample"), 120, LVCFMT_LEFT}, {_T("Status"), 54, LVCFMT_LEFT}, {_T("External Filename"), 262, LVCFMT_LEFT}, }; m_List.SetHeaders(headers); m_List.SetExtendedStyle(m_List.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES); GenerateList(); SetWindowText((_T("Modified External Samples - ") + m_modDoc.GetPathNameMpt().GetFullFileName().AsNative()).c_str()); return TRUE; } void ModifiedExternalSamplesDlg::GenerateList() { m_List.SetRedraw(FALSE); m_List.DeleteAllItems(); CString s; mpt::winstring status; for(SAMPLEINDEX smp = 1; smp <= m_sndFile.GetNumSamples(); smp++) { if(!m_sndFile.GetSample(smp).uFlags[SMP_KEEPONDISK]) continue; if(m_sndFile.GetSample(smp).uFlags[SMP_MODIFIED]) status = _T("modified"); else if(!m_sndFile.GetSamplePath(smp).IsFile()) status = _T("missing"); else continue; s.Format(_T("%02u: "), smp); s += mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.GetSampleName(smp)); int insertAt = m_List.InsertItem(m_List.GetItemCount(), s); if(insertAt == -1) continue; m_List.SetItemText(insertAt, 1, status.c_str()); m_List.SetItemText(insertAt, 2, m_sndFile.GetSamplePath(smp).AsNative().c_str()); m_List.SetCheck(insertAt, TRUE); m_List.SetItemData(insertAt, smp); } m_List.SetRedraw(TRUE); CheckDlgButton(IDC_CHECK1, BST_CHECKED); OnSelectionChanged(nullptr, nullptr); // Nothing modified? if(!m_List.GetItemCount()) OnOK(); } void ModifiedExternalSamplesDlg::OnCheckAll() { const BOOL check = IsDlgButtonChecked(IDC_CHECK1) ? TRUE : FALSE; const int count = m_List.GetItemCount(); for(int i = 0; i < count; i++) { m_List.SetCheck(i, check); } } void ModifiedExternalSamplesDlg::OnSelectionChanged(NMHDR *, LRESULT *) { int numChecked = 0; const int count = m_List.GetItemCount(); for(int i = 0; i < count; i++) { if(m_List.GetCheck(i)) numChecked++; } const TCHAR *embedText, *saveText; if(numChecked == count) { embedText = _T("&Embed All"); saveText = _T("&Save All"); } else if(!numChecked) { embedText = _T("&Embed None"); saveText = _T("&Save None"); } else { embedText = _T("&Embed Selected"); saveText = _T("&Save Selected"); } GetDlgItem(IDOK)->SetWindowText(embedText); GetDlgItem(IDC_SAVE)->SetWindowText(saveText); } void ModifiedExternalSamplesDlg::Execute(bool doSave) { ScopedLogCapturer log(m_modDoc, _T("Modified Samples"), this); bool ok = true; const int count = m_List.GetItemCount(); for(int i = 0; i < count; i++) { if(!m_List.GetCheck(i)) continue; SAMPLEINDEX smp = static_cast(m_List.GetItemData(i)); if(doSave) { ok &= m_modDoc.SaveSample(smp); } else { m_sndFile.GetSample(smp).uFlags.reset(SMP_KEEPONDISK); m_modDoc.SetModified(); } } if(ok) ResizableDialog::OnOK(); else ResizableDialog::OnCancel(); } OPENMPT_NAMESPACE_END