mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-07 07:12:34 +02:00
636 lines
18 KiB
C++
636 lines
18 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
FileNames.cpp
|
|
|
|
James Crook
|
|
|
|
********************************************************************//**
|
|
|
|
\class FileNames
|
|
\brief Provides Static functions to yield filenames.
|
|
|
|
This class helps us with setting a base path, and makes it easier
|
|
for us to keep track of the different kinds of files we read and write
|
|
from.
|
|
|
|
JKC: In time I plan to add all file names and file extensions
|
|
used throughout Audacity into this one place.
|
|
|
|
*//********************************************************************/
|
|
|
|
#include "Audacity.h"
|
|
#include "FileNames.h"
|
|
|
|
#include "Experimental.h"
|
|
|
|
#include "MemoryX.h"
|
|
|
|
#include <wx/app.h>
|
|
#include <wx/defs.h>
|
|
#include <wx/filename.h>
|
|
#include <wx/intl.h>
|
|
#include <wx/stdpaths.h>
|
|
#include "Prefs.h"
|
|
#include "Internat.h"
|
|
#include "PlatformCompatibility.h"
|
|
#include "wxFileNameWrapper.h"
|
|
#include "widgets/AudacityMessageBox.h"
|
|
#include "../lib-src/FileDialog/FileDialog.h"
|
|
|
|
#if defined(__WXMAC__) || defined(__WXGTK__)
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
#if defined(__WXMSW__)
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
static wxString gDataDir;
|
|
|
|
bool FileNames::CopyFile(
|
|
const FilePath& file1, const FilePath& file2, bool overwrite)
|
|
{
|
|
#ifdef __WXMSW__
|
|
|
|
// workaround not needed
|
|
return wxCopyFile(file1, file2, overwrite);
|
|
|
|
#else
|
|
// PRL: Compensate for buggy wxCopyFile that returns false success,
|
|
// which was a cause of case 4 in comment 10 of
|
|
// http://bugzilla.audacityteam.org/show_bug.cgi?id=1759
|
|
// Destination file was created, but was empty
|
|
// Bug was introduced after wxWidgets 2.8.12 at commit
|
|
// 0597e7f977c87d107e24bf3e95ebfa3d60efc249 of wxWidgets repo
|
|
|
|
bool existed = wxFileExists(file2);
|
|
bool result = wxCopyFile(file1, file2, overwrite) &&
|
|
wxFile{ file1 }.Length() == wxFile{ file2 }.Length();
|
|
if (!result && !existed)
|
|
wxRemoveFile(file2);
|
|
return result;
|
|
|
|
#endif
|
|
}
|
|
|
|
bool FileNames::HardLinkFile( const FilePath& file1, const FilePath& file2 )
|
|
{
|
|
#ifdef __WXMSW__
|
|
|
|
// Fix forced ASCII conversions and wrong argument order - MJB - 29/01/2019
|
|
//return ::CreateHardLinkA( file1.c_str(), file2.c_str(), NULL );
|
|
return ( 0 != ::CreateHardLink( file2, file1, NULL ) );
|
|
|
|
#else
|
|
|
|
return 0 == ::link( file1.c_str(), file2.c_str() );
|
|
|
|
#endif
|
|
}
|
|
|
|
wxString FileNames::MkDir(const wxString &Str)
|
|
{
|
|
// Behaviour of wxFileName::DirExists() and wxFileName::MkDir() has
|
|
// changed between wx2.6 and wx2.8, so we use static functions instead.
|
|
if (!wxFileName::DirExists(Str))
|
|
wxFileName::Mkdir(Str, 511, wxPATH_MKDIR_FULL);
|
|
|
|
return Str;
|
|
}
|
|
|
|
/// Returns the directory used for temp files.
|
|
/// \todo put a counter in here to see if it gets used a lot.
|
|
/// if it does, then maybe we should cache the path name
|
|
/// each time.
|
|
wxString FileNames::TempDir()
|
|
{
|
|
return FileNames::MkDir(gPrefs->Read(wxT("/Directories/TempDir"), wxT("")));
|
|
}
|
|
|
|
// originally an ExportMultiple method. Append suffix if newName appears in otherNames.
|
|
void FileNames::MakeNameUnique(FilePaths &otherNames,
|
|
wxFileName &newName)
|
|
{
|
|
if (otherNames.Index(newName.GetFullName(), false) >= 0) {
|
|
int i=2;
|
|
wxString orig = newName.GetName();
|
|
do {
|
|
newName.SetName(wxString::Format(wxT("%s-%d"), orig, i));
|
|
i++;
|
|
} while (otherNames.Index(newName.GetFullName(), false) >= 0);
|
|
}
|
|
otherNames.push_back(newName.GetFullName());
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Audacity user data directories
|
|
FilePath FileNames::AutoSaveDir()
|
|
{
|
|
wxFileName autoSaveDir(FileNames::DataDir(), wxT("AutoSave"));
|
|
return FileNames::MkDir(autoSaveDir.GetFullPath());
|
|
}
|
|
|
|
// The APP name has upercase first letter (so that Quit Audacity is correctly
|
|
// capitalised on Mac, but we want lower case APP name in paths.
|
|
// This function does that substitution, IF the last component of
|
|
// the path is 'Audacity'.
|
|
wxString FileNames::LowerCaseAppNameInPath( const wxString & dirIn){
|
|
wxString dir = dirIn;
|
|
// BUG 1577 Capitalisation of Audacity in path...
|
|
if( dir.EndsWith( "Audacity" ) )
|
|
{
|
|
int nChars = dir.length() - wxString( "Audacity" ).length();
|
|
dir = dir.Left( nChars ) + "audacity";
|
|
}
|
|
return dir;
|
|
}
|
|
|
|
FilePath FileNames::DataDir()
|
|
{
|
|
// LLL: Wouldn't you know that as of WX 2.6.2, there is a conflict
|
|
// between wxStandardPaths and wxConfig under Linux. The latter
|
|
// creates a normal file as "$HOME/.audacity", while the former
|
|
// expects the ".audacity" portion to be a directory.
|
|
if (gDataDir.empty())
|
|
{
|
|
// If there is a directory "Portable Settings" relative to the
|
|
// executable's EXE file, the prefs are stored in there, otherwise
|
|
// the prefs are stored in the user data dir provided by the OS.
|
|
wxFileName exePath(PlatformCompatibility::GetExecutablePath());
|
|
#if defined(__WXMAC__)
|
|
// Path ends for example in "Audacity.app/Contents/MacOSX"
|
|
//exePath.RemoveLastDir();
|
|
//exePath.RemoveLastDir();
|
|
// just remove the MacOSX part.
|
|
exePath.RemoveLastDir();
|
|
#endif
|
|
wxFileName portablePrefsPath(exePath.GetPath(), wxT("Portable Settings"));
|
|
|
|
if (::wxDirExists(portablePrefsPath.GetFullPath()))
|
|
{
|
|
// Use "Portable Settings" folder
|
|
gDataDir = portablePrefsPath.GetFullPath();
|
|
} else
|
|
{
|
|
// Use OS-provided user data dir folder
|
|
wxString dataDir( LowerCaseAppNameInPath( wxStandardPaths::Get().GetUserDataDir() ));
|
|
#if defined( __WXGTK__ )
|
|
dataDir = dataDir + wxT("-data");
|
|
#endif
|
|
gDataDir = FileNames::MkDir(dataDir);
|
|
}
|
|
}
|
|
return gDataDir;
|
|
}
|
|
|
|
FilePath FileNames::ResourcesDir(){
|
|
wxString resourcesDir( LowerCaseAppNameInPath( wxStandardPaths::Get().GetResourcesDir() ));
|
|
return resourcesDir;
|
|
}
|
|
|
|
FilePath FileNames::HtmlHelpDir()
|
|
{
|
|
#if defined(__WXMAC__)
|
|
wxFileName exePath(PlatformCompatibility::GetExecutablePath());
|
|
// Path ends for example in "Audacity.app/Contents/MacOSX"
|
|
//exePath.RemoveLastDir();
|
|
//exePath.RemoveLastDir();
|
|
// just remove the MacOSX part.
|
|
exePath.RemoveLastDir();
|
|
|
|
//for mac this puts us within the .app: Audacity.app/Contents/SharedSupport/
|
|
return wxFileName( exePath.GetPath()+wxT("/help/manual"), wxEmptyString ).GetFullPath();
|
|
#else
|
|
//linux goes into /*prefix*/share/audacity/
|
|
//windows (probably) goes into the dir containing the .exe
|
|
wxString dataDir = FileNames::LowerCaseAppNameInPath( wxStandardPaths::Get().GetDataDir());
|
|
return wxFileName( dataDir+wxT("/help/manual"), wxEmptyString ).GetFullPath();
|
|
#endif
|
|
}
|
|
|
|
FilePath FileNames::LegacyChainDir()
|
|
{
|
|
// Don't force creation of it
|
|
return wxFileName{ DataDir(), wxT("Chains") }.GetFullPath();
|
|
}
|
|
|
|
FilePath FileNames::MacroDir()
|
|
{
|
|
return FileNames::MkDir( wxFileName( DataDir(), wxT("Macros") ).GetFullPath() );
|
|
}
|
|
|
|
FilePath FileNames::NRPDir()
|
|
{
|
|
return FileNames::MkDir( wxFileName( DataDir(), wxT("NRP") ).GetFullPath() );
|
|
}
|
|
|
|
FilePath FileNames::NRPFile()
|
|
{
|
|
return wxFileName( NRPDir(), wxT("noisegate.nrp") ).GetFullPath();
|
|
}
|
|
|
|
FilePath FileNames::PlugInDir()
|
|
{
|
|
return FileNames::MkDir( wxFileName( DataDir(), wxT("Plug-Ins") ).GetFullPath() );
|
|
}
|
|
|
|
FilePath FileNames::PluginRegistry()
|
|
{
|
|
return wxFileName( DataDir(), wxT("pluginregistry.cfg") ).GetFullPath();
|
|
}
|
|
|
|
FilePath FileNames::PluginSettings()
|
|
{
|
|
return wxFileName( DataDir(), wxT("pluginsettings.cfg") ).GetFullPath();
|
|
}
|
|
|
|
FilePath FileNames::BaseDir()
|
|
{
|
|
wxFileName baseDir;
|
|
|
|
#if defined(__WXMAC__)
|
|
baseDir = PlatformCompatibility::GetExecutablePath();
|
|
|
|
// Path ends for example in "Audacity.app/Contents/MacOSX"
|
|
//baseDir.RemoveLastDir();
|
|
//baseDir.RemoveLastDir();
|
|
// just remove the MacOSX part.
|
|
baseDir.RemoveLastDir();
|
|
#elif defined(__WXMSW__)
|
|
// Don't use wxStandardPaths::Get().GetDataDir() since it removes
|
|
// the "Debug" directory in debug builds.
|
|
baseDir = PlatformCompatibility::GetExecutablePath();
|
|
#else
|
|
// Linux goes into /*prefix*/share/audacity/
|
|
baseDir = FileNames::LowerCaseAppNameInPath(wxStandardPaths::Get().GetDataDir());
|
|
#endif
|
|
|
|
return baseDir.GetPath();
|
|
}
|
|
|
|
FilePath FileNames::ModulesDir()
|
|
{
|
|
wxFileName modulesDir(BaseDir(), wxEmptyString);
|
|
|
|
modulesDir.AppendDir(wxT("modules"));
|
|
|
|
return modulesDir.GetFullPath();
|
|
}
|
|
|
|
FilePath FileNames::ThemeDir()
|
|
{
|
|
return FileNames::MkDir( wxFileName( DataDir(), wxT("Theme") ).GetFullPath() );
|
|
}
|
|
|
|
FilePath FileNames::ThemeComponentsDir()
|
|
{
|
|
return FileNames::MkDir( wxFileName( ThemeDir(), wxT("Components") ).GetFullPath() );
|
|
}
|
|
|
|
FilePath FileNames::ThemeCachePng()
|
|
{
|
|
return wxFileName( ThemeDir(), wxT("ImageCache.png") ).GetFullPath();
|
|
}
|
|
|
|
FilePath FileNames::ThemeCacheHtm()
|
|
{
|
|
return wxFileName( ThemeDir(), wxT("ImageCache.htm") ).GetFullPath();
|
|
}
|
|
|
|
FilePath FileNames::ThemeImageDefsAsCee()
|
|
{
|
|
return wxFileName( ThemeDir(), wxT("ThemeImageDefsAsCee.h") ).GetFullPath();
|
|
}
|
|
|
|
FilePath FileNames::ThemeCacheAsCee( )
|
|
{
|
|
// DA: Theme sourcery file name.
|
|
#ifndef EXPERIMENTAL_DA
|
|
return wxFileName( ThemeDir(), wxT("ThemeAsCeeCode.h") ).GetFullPath();
|
|
#else
|
|
return wxFileName( ThemeDir(), wxT("DarkThemeAsCeeCode.h") ).GetFullPath();
|
|
#endif
|
|
}
|
|
|
|
FilePath FileNames::ThemeComponent(const wxString &Str)
|
|
{
|
|
return wxFileName( ThemeComponentsDir(), Str, wxT("png") ).GetFullPath();
|
|
}
|
|
|
|
//
|
|
// Returns the full path of program module (.exe, .dll, .so, .dylib) containing address
|
|
//
|
|
FilePath FileNames::PathFromAddr(void *addr)
|
|
{
|
|
wxFileName name;
|
|
|
|
#if defined(__WXMAC__) || defined(__WXGTK__)
|
|
Dl_info info;
|
|
if (dladdr(addr, &info)) {
|
|
char realname[PLATFORM_MAX_PATH + 1];
|
|
int len;
|
|
name = LAT1CTOWX(info.dli_fname);
|
|
len = readlink(OSINPUT(name.GetFullPath()), realname, PLATFORM_MAX_PATH);
|
|
if (len > 0) {
|
|
realname[len] = 0;
|
|
name.SetFullName(LAT1CTOWX(realname));
|
|
}
|
|
}
|
|
#elif defined(__WXMSW__) && defined(_UNICODE)
|
|
// The GetModuleHandlEx() function did not appear until Windows XP and
|
|
// GetModuleFileName() did appear until Windows 2000, so we have to
|
|
// check for them at runtime.
|
|
typedef BOOL (WINAPI *getmodulehandleex)(DWORD dwFlags, LPCWSTR lpModuleName, HMODULE* phModule);
|
|
typedef DWORD (WINAPI *getmodulefilename)(HMODULE hModule, LPWCH lpFilename, DWORD nSize);
|
|
getmodulehandleex gmhe =
|
|
(getmodulehandleex) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
|
|
"GetModuleHandleExW");
|
|
getmodulefilename gmfn =
|
|
(getmodulefilename) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
|
|
"GetModuleFileNameW");
|
|
|
|
if (gmhe != NULL && gmfn != NULL) {
|
|
HMODULE module;
|
|
if (gmhe(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
|
(LPTSTR) addr,
|
|
&module)) {
|
|
TCHAR path[MAX_PATH];
|
|
DWORD nSize;
|
|
|
|
nSize = gmfn(module, path, MAX_PATH);
|
|
if (nSize && nSize < MAX_PATH) {
|
|
name = LAT1CTOWX(path);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return name.GetFullPath();
|
|
}
|
|
|
|
|
|
bool FileNames::IsPathAvailable( const FilePath & Path){
|
|
if( Path.IsEmpty() )
|
|
return false;
|
|
#ifndef __WIN32__
|
|
return true;
|
|
#else
|
|
wxFileNameWrapper filePath( Path );
|
|
return filePath.DirExists() && !filePath.FileExists();
|
|
#endif
|
|
}
|
|
|
|
wxFileNameWrapper FileNames::DefaultToDocumentsFolder(const wxString &preference)
|
|
{
|
|
wxFileNameWrapper result;
|
|
|
|
#ifdef __WIN32__
|
|
wxFileName defaultPath( wxStandardPaths::Get().GetDocumentsDir(), "" );
|
|
defaultPath.AppendDir( wxTheApp->GetAppName() );
|
|
result.SetPath( gPrefs->Read( preference, defaultPath.GetPath( wxPATH_GET_VOLUME ) ) );
|
|
|
|
// MJB: Bug 1899 & Bug 2007. Only create directory if the result is the default path
|
|
bool bIsDefaultPath = result == defaultPath;
|
|
if( !bIsDefaultPath )
|
|
{
|
|
// IF the prefs directory doesn't exist - (Deleted by our user perhaps?)
|
|
// or exists as a file
|
|
// THEN fallback to using the default directory.
|
|
bIsDefaultPath = !IsPathAvailable( result.GetPath(wxPATH_GET_VOLUME|wxPATH_GET_SEPARATOR ) );
|
|
if( bIsDefaultPath )
|
|
{
|
|
result.SetPath( defaultPath.GetPath( wxPATH_GET_VOLUME ) );
|
|
// Don't write to gPrefs.
|
|
// We typically do it later, (if directory actually gets used)
|
|
}
|
|
}
|
|
if ( bIsDefaultPath )
|
|
{
|
|
// The default path might not exist since it is a sub-directory of 'Documents'
|
|
// There is no error if the path could not be created. That's OK.
|
|
// The dialog that Audacity offers will allow the user to select a valid directory.
|
|
result.Mkdir(0755, wxPATH_MKDIR_FULL);
|
|
}
|
|
#else
|
|
result.AssignHomeDir();
|
|
result.SetPath(gPrefs->Read( preference, result.GetPath() + "/Documents"));
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
namespace {
|
|
wxString PreferenceKey(FileNames::Operation op)
|
|
{
|
|
wxString key;
|
|
switch (op) {
|
|
case FileNames::Operation::Open:
|
|
key = wxT("/DefaultOpenPath"); break;
|
|
case FileNames::Operation::Export:
|
|
key = wxT("/DefaultExportPath"); break;
|
|
case FileNames::Operation::_None:
|
|
default:
|
|
break;
|
|
}
|
|
return key;
|
|
}
|
|
}
|
|
|
|
wxString FileNames::FindDefaultPath(Operation op)
|
|
{
|
|
auto key = PreferenceKey(op);
|
|
if (key.empty())
|
|
return wxString{};
|
|
else
|
|
return DefaultToDocumentsFolder(key).GetPath();
|
|
}
|
|
|
|
void FileNames::UpdateDefaultPath(Operation op, const FilePath &path)
|
|
{
|
|
if (path.empty())
|
|
return;
|
|
auto key = PreferenceKey(op);
|
|
if (!key.empty()) {
|
|
gPrefs->Write(key, ::wxPathOnly(path));
|
|
gPrefs->Flush();
|
|
}
|
|
}
|
|
|
|
wxString
|
|
FileNames::SelectFile(Operation op,
|
|
const wxString& message,
|
|
const FilePath& default_path,
|
|
const FilePath& default_filename,
|
|
const wxString& default_extension,
|
|
const wxString& wildcard,
|
|
int flags,
|
|
wxWindow *parent)
|
|
{
|
|
return WithDefaultPath(op, default_path, [&](const FilePath &path) {
|
|
return FileSelector(
|
|
message, path, default_filename, default_extension,
|
|
wildcard, flags, parent, wxDefaultCoord, wxDefaultCoord);
|
|
});
|
|
}
|
|
|
|
/** \brief Default temp directory */
|
|
static FilePath sDefaultTempDir;
|
|
|
|
const FilePath &FileNames::DefaultTempDir()
|
|
{
|
|
return sDefaultTempDir;
|
|
}
|
|
|
|
void FileNames::SetDefaultTempDir( const FilePath &tempDir )
|
|
{
|
|
sDefaultTempDir = tempDir;
|
|
}
|
|
|
|
// We now disallow temp directory name that puts it where cleaner apps will
|
|
// try to clean out the files.
|
|
bool FileNames::IsTempDirectoryNameOK( const FilePath & Name )
|
|
{
|
|
if( Name.empty() )
|
|
return false;
|
|
|
|
wxFileName tmpFile;
|
|
tmpFile.AssignTempFileName(wxT("nn"));
|
|
// use Long Path to expand out any abbreviated long substrings.
|
|
wxString BadPath = tmpFile.GetLongPath();
|
|
::wxRemoveFile(tmpFile.GetFullPath());
|
|
|
|
#ifdef __WXMAC__
|
|
// This test is to fix bug 1220 on a 1.x to 2.x to 2.1.3 upgrade.
|
|
// It is less permissive than we could be as it stops a path
|
|
// with this string ANYWHERE within it rather than excluding just
|
|
// the paths that the earlier Audacities used to create.
|
|
if( Name.Contains( "/tmp/") )
|
|
return false;
|
|
BadPath = BadPath.BeforeLast( '/' ) + "/";
|
|
wxFileName cmpFile( Name );
|
|
wxString NameCanonical = cmpFile.GetLongPath( ) + "/";
|
|
#else
|
|
BadPath = BadPath.BeforeLast( '\\' ) + "\\";
|
|
wxFileName cmpFile( Name );
|
|
wxString NameCanonical = cmpFile.GetLongPath( ) + "\\";
|
|
#endif
|
|
return !(NameCanonical.StartsWith( BadPath ));
|
|
}
|
|
|
|
bool FileNames::IsMidi(const FilePath &fName)
|
|
{
|
|
const auto extension = fName.AfterLast(wxT('.'));
|
|
return
|
|
extension.IsSameAs(wxT("gro"), false) ||
|
|
extension.IsSameAs(wxT("midi"), false) ||
|
|
extension.IsSameAs(wxT("mid"), false);
|
|
}
|
|
|
|
static FilePaths sAudacityPathList;
|
|
|
|
const FilePaths &FileNames::AudacityPathList()
|
|
{
|
|
return sAudacityPathList;
|
|
}
|
|
|
|
void FileNames::SetAudacityPathList( FilePaths list )
|
|
{
|
|
sAudacityPathList = std::move( list );
|
|
}
|
|
|
|
// static
|
|
void FileNames::AddUniquePathToPathList(const FilePath &pathArg,
|
|
FilePaths &pathList)
|
|
{
|
|
wxFileNameWrapper pathNorm { pathArg };
|
|
pathNorm.Normalize();
|
|
const wxString newpath{ pathNorm.GetFullPath() };
|
|
|
|
for(const auto &path : pathList) {
|
|
if (pathNorm == wxFileNameWrapper{ path })
|
|
return;
|
|
}
|
|
|
|
pathList.push_back(newpath);
|
|
}
|
|
|
|
// static
|
|
void FileNames::AddMultiPathsToPathList(const wxString &multiPathStringArg,
|
|
FilePaths &pathList)
|
|
{
|
|
wxString multiPathString(multiPathStringArg);
|
|
while (!multiPathString.empty()) {
|
|
wxString onePath = multiPathString.BeforeFirst(wxPATH_SEP[0]);
|
|
multiPathString = multiPathString.AfterFirst(wxPATH_SEP[0]);
|
|
AddUniquePathToPathList(onePath, pathList);
|
|
}
|
|
}
|
|
|
|
#include <wx/log.h>
|
|
|
|
// static
|
|
void FileNames::FindFilesInPathList(const wxString & pattern,
|
|
const FilePaths & pathList,
|
|
FilePaths & results,
|
|
int flags)
|
|
{
|
|
wxLogNull nolog;
|
|
|
|
if (pattern.empty()) {
|
|
return;
|
|
}
|
|
|
|
wxFileNameWrapper ff;
|
|
|
|
for(size_t i = 0; i < pathList.size(); i++) {
|
|
ff = pathList[i] + wxFILE_SEP_PATH + pattern;
|
|
wxDir::GetAllFiles(ff.GetPath(), &results, ff.GetFullName(), flags);
|
|
}
|
|
}
|
|
|
|
#if defined(__WXMSW__)
|
|
wxCharBuffer FileNames::mFilename;
|
|
|
|
//
|
|
// On Windows, wxString::mb_str() can return a NULL pointer if the
|
|
// conversion to multi-byte fails. So, based on direction intent,
|
|
// returns a pointer to an empty string or prompts for a NEW name.
|
|
//
|
|
char *FileNames::VerifyFilename(const wxString &s, bool input)
|
|
{
|
|
static wxCharBuffer buf;
|
|
wxString name = s;
|
|
|
|
if (input) {
|
|
if ((char *) (const char *)name.mb_str() == NULL) {
|
|
name = wxEmptyString;
|
|
}
|
|
}
|
|
else {
|
|
wxFileName ff(name);
|
|
wxString ext;
|
|
while ((char *) (const char *)name.mb_str() == NULL) {
|
|
AudacityMessageBox(_("The specified filename could not be converted due to Unicode character use."));
|
|
|
|
ext = ff.GetExt();
|
|
name = FileNames::SelectFile(FileNames::Operation::_None,
|
|
_("Specify New Filename:"),
|
|
wxEmptyString,
|
|
name,
|
|
ext,
|
|
wxT("*.") + ext,
|
|
wxFD_SAVE | wxRESIZE_BORDER,
|
|
wxGetTopLevelParent(NULL));
|
|
}
|
|
}
|
|
|
|
mFilename = name.mb_str();
|
|
|
|
return (char *) (const char *) mFilename;
|
|
}
|
|
#endif
|