368 lines
9.4 KiB
C++
368 lines
9.4 KiB
C++
|
/*
|
||
|
* 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<SAMPLEINDEX>(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<SAMPLEINDEX>(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
|