1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-20 17:41:13 +02:00

New library for files

This commit is contained in:
Paul Licameli
2021-02-18 10:22:00 -05:00
parent 4fd6e8a151
commit 0506faee02
54 changed files with 145 additions and 130 deletions

View File

@@ -0,0 +1,135 @@
/**********************************************************************
Audacity: A Digital Audio Editor
AudacityLogger.cpp
******************************************************************//**
\class AudacityLogger
\brief AudacityLogger is a thread-safe logger class
Provides thread-safe logging based on the wxWidgets log facility.
*//*******************************************************************/
#include "AudacityLogger.h"
#include "Internat.h"
#include "MemoryX.h"
#include <memory>
#include <mutex>
#include <wx/ffile.h>
#include <wx/log.h>
#include <wx/tokenzr.h>
//
// AudacityLogger class
//
// By providing an Audacity specific logging class, it can be made thread-safe and,
// as such, can be used by the ever growing threading within Audacity.
//
AudacityLogger *AudacityLogger::Get()
{
static std::once_flag flag;
std::call_once( flag, []{
// wxWidgets will clean up the logger for the main thread, so we can say
// safenew. See:
// http://docs.wxwidgets.org/3.0/classwx_log.html#a2525bf54fa3f31dc50e6e3cd8651e71d
std::unique_ptr < wxLog > // DELETE any previous logger
{ wxLog::SetActiveTarget(safenew AudacityLogger) };
} );
// Use dynamic_cast so that we get a NULL ptr in case our logger
// is no longer the target.
return dynamic_cast<AudacityLogger *>(wxLog::GetActiveTarget());
}
AudacityLogger::AudacityLogger()
: wxEvtHandler(),
wxLog()
{
mUpdated = false;
}
AudacityLogger::~AudacityLogger() = default;
void AudacityLogger::Flush()
{
if (mUpdated && mListener && mListener())
mUpdated = false;
}
auto AudacityLogger::SetListener(Listener listener) -> Listener
{
auto result = std::move(mListener);
mListener = std::move(listener);
return result;
}
void AudacityLogger::DoLogText(const wxString & str)
{
if (!wxIsMainThread()) {
wxMutexGuiEnter();
}
if (mBuffer.empty()) {
wxString stamp;
TimeStamp(&stamp);
mBuffer << stamp << _TS("Audacity ") << AUDACITY_VERSION_STRING << wxT("\n");
}
mBuffer << str << wxT("\n");
mUpdated = true;
Flush();
if (!wxIsMainThread()) {
wxMutexGuiLeave();
}
}
bool AudacityLogger::SaveLog(const wxString &fileName) const
{
wxFFile file(fileName, wxT("w"));
if (file.IsOpened()) {
file.Write(mBuffer);
file.Close();
return true;
}
return false;
}
bool AudacityLogger::ClearLog()
{
mBuffer = wxEmptyString;
DoLogText(wxT("Log Cleared."));
return true;
}
wxString AudacityLogger::GetLog(int count)
{
if (count == 0)
{
return mBuffer;
}
wxString buffer;
auto lines = wxStringTokenize(mBuffer, wxT("\r\n"), wxTOKEN_RET_DELIMS);
for (int index = lines.size() - 1; index >= 0 && count > 0; --index, --count)
{
buffer.Prepend(lines[index]);
}
return buffer;
}

View File

@@ -0,0 +1,61 @@
/**********************************************************************
Audacity: A Digital Audio Editor
AudacityLogger.h
Dominic Mazzoni
**********************************************************************/
#ifndef __AUDACITY_LOGGER__
#define __AUDACITY_LOGGER__
#include <wx/log.h> // to inherit
#include <wx/event.h> // to inherit wxEvtHandler
class wxFrame;
class wxTextCtrl;
class FILES_API AudacityLogger final : public wxEvtHandler,
public wxLog
{
public:
~AudacityLogger() override;
// Get the singleton instance or null
static AudacityLogger *Get();
bool SaveLog(const wxString &fileName) const;
bool ClearLog();
//! Retrieve all or some of the lines since most recent ClearLog or start of program
/*! If `count == 0` or is more than the number of lines, return all; else return the last `count` lines */
wxString GetLog(int count = 0);
//! Get all the accumulated text since program start or last ClearLog()
const wxString &GetBuffer() const { return mBuffer; }
void Flush() override;
//! Type of function called by Flush
/*! @return true if flush completed
*/
using Listener = std::function< bool() >;
//! Set the unique listener, returning any previous one
Listener SetListener(Listener listener);
protected:
void DoLogText(const wxString & msg) override;
private:
AudacityLogger();
Listener mListener;
wxString mBuffer;
bool mUpdated;
};
#endif

View File

@@ -0,0 +1,34 @@
#[[
A library of utilities relating generally to files and file names, including
an exception type for file errors.
Also the definition of certain significant file paths, such as the directory
for temporary projects.
Also the global logger which can save to a file.
]]#
set( SOURCES
AudacityLogger.cpp
AudacityLogger.h
FileException.cpp
FileException.h
FileIO.cpp
FileIO.h
FileNames.cpp
FileNames.h
PlatformCompatibility.cpp
PlatformCompatibility.h
TempDirectory.cpp
TempDirectory.h
wxFileNameWrapper.h
)
set( LIBRARIES
lib-exceptions-interface
lib-preferences-interface
PRIVATE
wxBase
)
audacity_library( lib-files "${SOURCES}" "${LIBRARIES}"
"" ""
)

View File

@@ -0,0 +1,66 @@
/*!
@file FileException.cpp
@brief implements FileException
Created by Paul Licameli on 11/22/16.
*/
#include "FileException.h"
#include "FileNames.h"
#include "Prefs.h"
FileException::~FileException()
{
}
TranslatableString FileException::ErrorMessage() const
{
TranslatableString format;
switch (cause) {
case Cause::Open:
format = XO("Audacity failed to open a file in %s.");
break;
case Cause::Read:
format = XO("Audacity failed to read from a file in %s.");
break;
case Cause::Write:
return WriteFailureMessage(fileName);
case Cause::Rename:
format =
XO("Audacity successfully wrote a file in %s but failed to rename it as %s.");
default:
break;
}
return format.Format(
FileNames::AbbreviatePath(fileName), renameTarget.GetFullName() );
}
wxString FileException::ErrorHelpUrl() const
{
switch (cause) {
case Cause::Open:
case Cause::Read:
return "Error:_Opening_or_reading_file";
break;
case Cause::Write:
case Cause::Rename:
return "Error:_Disk_full_or_not_writable";
default:
break;
}
return "";
}
TranslatableString
FileException::WriteFailureMessage(const wxFileName &fileName)
{
return XO("Audacity failed to write to a file.\n"
"Perhaps %s is not writable or the disk is full.\n"
"For tips on freeing up space, click the help button."
).Format(FileNames::AbbreviatePath(fileName));
}

View File

@@ -0,0 +1,67 @@
/*!
@file FileException.h
@brief MessageBoxException for failures of file operations
Created by Paul Licameli on 11/22/16.
*/
#ifndef __AUDACITY_FILE_EXCEPTION__
#define __AUDACITY_FILE_EXCEPTION__
#include "AudacityException.h" // to inherit
#include <wx/filename.h> // wxFileName member variable
//! Thrown for failure of file or database operations in deeply nested places
class FILES_API FileException /* not final */
: public MessageBoxException
{
public:
//! Identifies file operation that failed
enum class Cause {
Open,
Read,
Write, //!< most important to detect when storage space is exhausted
Rename //!< involves two filenames
};
explicit FileException(
Cause cause_, //!< What kind of file operation failed
const wxFileName &fileName_, //!< Which file suffered a failure
const TranslatableString &caption = XO("File Error"), //!< Shown in message box frame, not the main message
const wxFileName &renameTarget_ = {} //!< A second file name, only for renaming failure
)
// DV: We consider a FileException to be internal for now.
// We used to have some odd cases related to the read-only folder in 3.0.0,
// so it is good to have a full picture here.
// In case we see too many - we will tweak this behavior in the next release.
: MessageBoxException{ ExceptionType::Internal, caption }
, cause{ cause_ }, fileName{ fileName_ }, renameTarget{ renameTarget_ }
{}
FileException( const FileException &that )
: MessageBoxException( that )
, cause{ that.cause }
, fileName{ that.fileName }
, renameTarget{ that.renameTarget }
{}
FileException& operator= (FileException&&) = delete;
~FileException() override;
static TranslatableString WriteFailureMessage(const wxFileName &fileName);
protected:
//! %Format an error message appropriate for the @ref Cause.
TranslatableString ErrorMessage() const override;
wxString ErrorHelpUrl() const override;
public:
Cause cause;
wxFileName fileName;
wxFileName renameTarget;
};
#endif

View File

@@ -0,0 +1,84 @@
/**********************************************************************
Audacity: A Digital Audio Editor
FileIO.cpp
Leland Lucius
**********************************************************************/
#include "FileIO.h"
#include <wx/defs.h>
#include <wx/crt.h>
#include <wx/filename.h>
#include <wx/wfstream.h>
#include "wxFileNameWrapper.h"
FileIO::FileIO(const wxFileNameWrapper & name, FileIOMode mode)
: mMode(mode),
mOpen(false)
{
wxString scheme;
auto path = name.GetFullPath();
if (mMode == FileIO::Input) {
mInputStream = std::make_unique<wxFFileInputStream>(path);
if (mInputStream == NULL || !mInputStream->IsOk()) {
wxPrintf(wxT("Couldn't get input stream: %s\n"), path);
return;
}
}
else {
mOutputStream = std::make_unique<wxFFileOutputStream>(path);
if (mOutputStream == NULL || !mOutputStream->IsOk()) {
wxPrintf(wxT("Couldn't get output stream: %s\n"), path);
return;
}
}
mOpen = true;
}
FileIO::~FileIO()
{
Close();
}
bool FileIO::IsOpened()
{
return mOpen;
}
bool FileIO::Close()
{
bool success = true;
if (mOutputStream) {
// mOutputStream->Sync() returns void! Rrr!
success = mOutputStream->GetFile()->Flush() &&
mOutputStream->Close();
mOutputStream.reset();
}
mInputStream.reset();
mOpen = false;
return success;
}
wxInputStream & FileIO::Read(void *buf, size_t size)
{
if (mInputStream == NULL) {
return *mInputStream;
}
return mInputStream->Read(buf, size);
}
wxOutputStream & FileIO::Write(const void *buf, size_t size)
{
if (mOutputStream == NULL) {
return *mOutputStream;
}
return mOutputStream->Write(buf, size);
}

View File

@@ -0,0 +1,50 @@
/**********************************************************************
Audacity: A Digital Audio Editor
FileIO.h
Leland Lucius
**********************************************************************/
#ifndef __AUDACITY_FILEIO__
#define __AUDACITY_FILEIO__
#include <memory>
class wxInputStream;
class wxOutputStream;
class wxFFileOutputStream;
class wxFileNameWrapper;
class FILES_API FileIO
{
public:
typedef enum FileIOMode
{
Input,
Output
} FileIOMode;
public:
FileIO(const wxFileNameWrapper & name, FileIOMode mode);
// Calls Close()
~FileIO();
bool IsOpened();
bool Close();
wxInputStream & Read(void *buffer, size_t size);
wxOutputStream & Write(const void *buffer, size_t size);
private:
FileIOMode mMode;
std::unique_ptr<wxInputStream> mInputStream;
std::unique_ptr<wxFFileOutputStream> mOutputStream;
bool mOpen;
};
#endif

View File

@@ -0,0 +1,767 @@
/**********************************************************************
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 "FileNames.h"
#include <memory>
#include <wx/defs.h>
#include <wx/filename.h>
#include <wx/intl.h>
#include <wx/stdpaths.h>
#include "BasicUI.h"
#include "Prefs.h"
#include "Internat.h"
#include "ModuleConstants.h"
#include "PlatformCompatibility.h"
#include "wxFileNameWrapper.h"
#if defined(__WXMAC__) || defined(__WXGTK__)
#include <dlfcn.h>
#endif
#if defined(__WXMSW__)
#include <windows.h>
#endif
static wxString gDataDir;
const FileNames::FileType
FileNames::AllFiles{ XO("All files"), { wxT("") } }
/* i18n-hint an Audacity project is the state of the program, stored as
files that can be reopened to resume the session later */
, FileNames::AudacityProjects{ XO("AUP3 project files"), { wxT("aup3") }, true }
, FileNames::DynamicLibraries{
#if defined(__WXMSW__)
XO("Dynamically Linked Libraries"), { wxT("dll") }, true
#elif defined(__WXMAC__)
XO("Dynamic Libraries"), { wxT("dylib") }, true
#else
XO("Dynamically Linked Libraries"), { wxT("so*") }, true
#endif
}
, FileNames::TextFiles{ XO("Text files"), { wxT("txt") }, true }
, FileNames::XMLFiles{ XO("XML files"), { wxT("xml"), wxT("XML") }, true }
;
wxString FileNames::FormatWildcard( const FileTypes &fileTypes )
{
// |-separated list of:
// [ Description,
// ( if appendExtensions, then ' (', globs, ')' ),
// '|',
// globs ]
// where globs is a ;-separated list of filename patterns, which are
// '*' for an empty extension, else '*.' then the extension
// Only the part before | is displayed in the choice drop-down of file
// dialogs
//
// Exceptional case: if there is only one type and its description is empty,
// then just give the globs with no |
// Another exception: an empty description, when there is more than one
// type, is replaced with a default
// Another exception: if an extension contains a dot, it is interpreted as
// not really an extension, but a literal filename
const wxString dot{ '.' };
const auto makeGlobs = [&dot]( const FileExtensions &extensions ){
wxString globs;
for ( const auto &extension: extensions ) {
if ( !globs.empty() )
globs += ';';
if ( extension.Contains( dot ) )
globs += extension;
else {
globs += '*';
if ( !extension.empty() ) {
globs += '.';
globs += extension;
}
}
}
return globs;
};
const auto defaultDescription = []( const FileExtensions &extensions ){
// Assume extensions is not empty
wxString exts = extensions[0];
for (size_t ii = 1, size = extensions.size(); ii < size; ++ii ) {
exts += XO(", ").Translation();
exts += extensions[ii];
}
/* i18n-hint a type or types such as "txt" or "txt, xml" will be
substituted for %s */
return XO("%s files").Format( exts );
};
if ( fileTypes.size() == 1 && fileTypes[0].description.empty() ) {
return makeGlobs( fileTypes[0].extensions );
}
else {
wxString result;
for ( const auto &fileType : fileTypes ) {
const auto &extensions = fileType.extensions;
if (extensions.empty())
continue;
if (!result.empty())
result += '|';
const auto globs = makeGlobs( extensions );
auto mask = fileType.description;
if ( mask.empty() )
mask = defaultDescription( extensions );
if ( fileType.appendExtensions )
mask.Join( XO("(%s)").Format( globs ), " " );
result += mask.Translation();
result += '|';
result += globs;
}
return result;
}
}
bool FileNames::DoCopyFile(
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;
}
// originally an ExportMultipleDialog 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());
}
// 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().GetPluginsDir());
#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__)
#define OSFILENAME(X) ((char *) (const char *)(X).fn_str())
#define OSINPUT(X) OSFILENAME(X)
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( AppName );
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;
}
wxString FileNames::PreferenceKey(FileNames::Operation op, FileNames::PathType type)
{
wxString key;
switch (op) {
case FileNames::Operation::Temp:
key = wxT("/Directories/TempDir"); break;
case FileNames::Operation::Presets:
key = wxT("/Presets/Path"); break;
case FileNames::Operation::Open:
key = wxT("/Directories/Open"); break;
case FileNames::Operation::Save:
key = wxT("/Directories/Save"); break;
case FileNames::Operation::Import:
key = wxT("/Directories/Import"); break;
case FileNames::Operation::Export:
key = wxT("/Directories/Export"); break;
case FileNames::Operation::MacrosOut:
key = wxT("/Directories/MacrosOut"); break;
case FileNames::Operation::_None:
default:
break;
}
switch (type) {
case FileNames::PathType::User:
key += "/Default"; break;
case FileNames::PathType::LastUsed:
key += "/LastUsed"; break;
case FileNames::PathType::_None:
default:
break;
}
return key;
}
FilePath FileNames::FindDefaultPath(Operation op)
{
auto key = PreferenceKey(op, PathType::User);
if (key.empty())
return wxString{};
// If the user specified a default path, then use that
FilePath path = gPrefs->Read(key, wxT(""));
if (!path.empty()) {
return path;
}
// Maybe the last used path is available
key = PreferenceKey(op, PathType::LastUsed);
path = gPrefs->Read(key, wxT(""));
if (!path.empty()) {
return path;
}
// Last resort is to simply return the default folder
return DefaultToDocumentsFolder("").GetPath();
}
void FileNames::UpdateDefaultPath(Operation op, const FilePath &path)
{
if (path.empty())
return;
wxString key;
if (op == Operation::Temp) {
key = PreferenceKey(op, PathType::_None);
}
else {
key = PreferenceKey(op, PathType::LastUsed);
}
if (!key.empty()) {
gPrefs->Write(key, path);
gPrefs->Flush();
}
}
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);
}
}
bool FileNames::WritableLocationCheck(const FilePath& path)
{
bool status = wxFileName::IsDirWritable(path);
if (!status)
{
using namespace BasicUI;
ShowMessageBox(
XO("Directory %s does not have write permissions").Format(path),
MessageBoxOptions{}
.Caption(XO("Error"))
.IconStyle(Icon::Error)
.ButtonStyle(Button::Ok)
);
}
return status;
}
// Using this with wxStringArray::Sort will give you a list that
// is alphabetical, without depending on case. If you use the
// default sort, you will get strings with 'R' before 'a', because it is in caps.
int FileNames::CompareNoCase(const wxString& first, const wxString& second)
{
return first.CmpNoCase(second);
}
// Create a unique filename using the passed prefix and suffix
wxString FileNames::CreateUniqueName(const wxString &prefix,
const wxString &suffix /* = wxEmptyString */)
{
static int count = 0;
return wxString::Format(wxT("%s %s N-%i.%s"),
prefix,
wxDateTime::Now().Format(wxT("%Y-%m-%d %H-%M-%S")),
++count,
suffix);
}
wxString FileNames::UnsavedProjectExtension()
{
return wxT("aup3unsaved");
}
// How to detect whether the file system of a path is FAT
// No apparent way to do it with wxWidgets
#if defined(__DARWIN__)
#include <sys/mount.h>
bool FileNames::IsOnFATFileSystem(const FilePath &path)
{
struct statfs fs;
if (statfs(wxPathOnly(path).c_str(), &fs))
// Error from statfs
return false;
return 0 == strcmp(fs.f_fstypename, "msdos");
}
#elif defined(__linux__)
#include <sys/statfs.h>
#include "/usr/include/linux/magic.h"
bool FileNames::IsOnFATFileSystem(const FilePath &path)
{
struct statfs fs;
if (statfs(wxPathOnly(path).c_str(), &fs))
// Error from statfs
return false;
return fs.f_type == MSDOS_SUPER_MAGIC;
}
#elif defined(_WIN32)
#include <fileapi.h>
bool FileNames::IsOnFATFileSystem(const FilePath &path)
{
wxFileNameWrapper fileName{path};
if (!fileName.HasVolume())
return false;
auto volume = AbbreviatePath(fileName) + wxT("\\");
DWORD volumeFlags;
wxChar volumeType[64];
if (!::GetVolumeInformationW(
volume.wc_str(), NULL, 0, NULL, NULL,
&volumeFlags,
volumeType,
WXSIZEOF(volumeType)))
return false;
wxString type(volumeType);
if (type == wxT("FAT") || type == wxT("FAT32"))
return true;
return false;
}
#else
bool FileNames::IsOnFATFileSystem(const FilePath &path)
{
return false;
}
#endif
wxString FileNames::AbbreviatePath( const wxFileName &fileName )
{
wxString target;
#ifdef __WXMSW__
// Drive letter plus colon
target = fileName.GetVolume() + wxT(":");
#else
// Shorten the path, arbitrarily to 3 components
auto path = fileName;
path.SetFullName(wxString{});
while(path.GetDirCount() > 3)
path.RemoveLastDir();
target = path.GetFullPath();
#endif
return target;
}

View File

@@ -0,0 +1,227 @@
/**********************************************************************
Audacity: A Digital Audio Editor
FileNames.h
James Crook
**********************************************************************/
#ifndef __AUDACITY_FILE_NAMES__
#define __AUDACITY_FILE_NAMES__
#include <wx/dir.h> // for wxDIR_FILES
#include <wx/string.h> // function return value
#include "Identifier.h"
#include "Prefs.h"
// Please try to support unlimited path length instead of using PLATFORM_MAX_PATH!
// Define one constant for maximum path value, so we don't have to do
// platform-specific conditionals everywhere we want to check it.
#define PLATFORM_MAX_PATH 260 // Play it safe for default, with same value as Windows' MAX_PATH.
#ifdef __WXMAC__
#undef PLATFORM_MAX_PATH
#define PLATFORM_MAX_PATH PATH_MAX
#endif
#ifdef __WXGTK__
// Some systems do not restrict the path length and therefore PATH_MAX is undefined
#ifdef PATH_MAX
#undef PLATFORM_MAX_PATH
#define PLATFORM_MAX_PATH PATH_MAX
#endif
#endif
#ifdef __WXX11__
// wxX11 should also get the platform-specific definition of PLATFORM_MAX_PATH, so do not declare here.
#endif
#ifdef __WXMSW__
#undef PLATFORM_MAX_PATH
#define PLATFORM_MAX_PATH MAX_PATH
#endif
class wxFileName;
class wxFileNameWrapper;
namespace FileNames
{
// A description of a type of file
struct FileType {
FileType() = default;
FileType( TranslatableString d, FileExtensions e, bool a = false )
: description{ std::move( d ) }
, extensions( std::move( e ) )
, appendExtensions{ a }
{}
TranslatableString description;
FileExtensions extensions;
// Whether to extend the displayed description with mention of the
// extensions:
bool appendExtensions = false;
};
// Frequently used types
extern FILES_API const FileType
AllFiles // *
, AudacityProjects // *.aup3
, DynamicLibraries // depends on the operating system
, TextFiles // *.txt
, XMLFiles; // *.xml, *.XML
using FileTypes = std::vector< FileType >;
// Convert fileTypes into a single string as expected by wxWidgets file
// selection dialog
FILES_API wxString FormatWildcard( const FileTypes &fileTypes );
// This exists to compensate for bugs in wxCopyFile:
FILES_API bool DoCopyFile(
const FilePath& file1, const FilePath& file2, bool overwrite = true);
// wxWidgets doesn't have a function to do this: make a hard file-system
// link if possible. It might not be, as when the paths are on different
// storage devices.
FILES_API
bool HardLinkFile( const FilePath& file1, const FilePath& file2);
FILES_API wxString MkDir(const wxString &Str);
FILES_API bool IsMidi(const FilePath &fName);
/** \brief A list of directories that should be searched for Audacity files
* (plug-ins, help files, etc.).
*
* On Unix this will include the directory Audacity was installed into,
* plus the current user's .audacity-data/Plug-Ins directory. Additional
* directories can be specified using the AUDACITY_PATH environment
* variable. On Windows or Mac OS, this will include the directory
* which contains the Audacity program. */
FILES_API const FilePaths &AudacityPathList();
FILES_API void SetAudacityPathList( FilePaths list );
// originally an ExportMultipleDialog method. Append suffix if newName appears in otherNames.
FILES_API void MakeNameUnique(
FilePaths &otherNames, wxFileName &newName);
FILES_API wxString LowerCaseAppNameInPath( const wxString & dirIn);
/** \brief Audacity user data directory
*
* Where audacity keeps its settings and other user data squirreled away,
* by default ~/.audacity-data/ on Unix, Application Data/Audacity on
* windows system */
FILES_API FilePath DataDir();
FILES_API FilePath ResourcesDir();
FILES_API FilePath HtmlHelpDir();
FILES_API FilePath HtmlHelpIndexFile(bool quick);
FILES_API FilePath LegacyChainDir();
FILES_API FilePath MacroDir();
FILES_API FilePath NRPDir();
FILES_API FilePath NRPFile();
FILES_API FilePath PluginRegistry();
FILES_API FilePath PluginSettings();
FILES_API FilePath BaseDir();
FILES_API FilePath ModulesDir();
/** \brief The user plug-in directory (not a system one)
*
* This returns the string path to where the user may have put plug-ins
* if they don't have system admin rights. Under default settings, it's
* <DataDir>/Plug-Ins/ */
FILES_API FilePath PlugInDir();
FILES_API FilePath ThemeDir();
FILES_API FilePath ThemeComponentsDir();
FILES_API FilePath ThemeCachePng();
FILES_API FilePath ThemeCacheAsCee();
FILES_API FilePath ThemeComponent(const wxString &Str);
FILES_API FilePath ThemeCacheHtm();
FILES_API FilePath ThemeImageDefsAsCee();
// Obtain name of loaded module that contains address
FILES_API FilePath PathFromAddr(void *addr);
FILES_API bool IsPathAvailable( const FilePath & Path);
FILES_API wxFileNameWrapper DefaultToDocumentsFolder
(const wxString &preference);
// If not None, determines a preference key (for the default path string) to
// be read and updated
enum class Operation {
// _ on None to defeat some macro that is expanding this.
_None,
// These do not have a specific pathtype
Temp,
Presets,
// These have default/lastused pathtypes
Open,
Save,
Import,
Export,
MacrosOut
};
enum class PathType {
// _ on None to defeat some macro that is expanding this.
_None,
User,
LastUsed
};
FILES_API wxString PreferenceKey(FileNames::Operation op, FileNames::PathType type);
FILES_API FilePath FindDefaultPath(Operation op);
FILES_API void UpdateDefaultPath(Operation op, const FilePath &path);
// F is a function taking a wxString, returning wxString
template<typename F>
FilePath WithDefaultPath
(Operation op, const FilePath &defaultPath, F function)
{
auto path = gPrefs->Read(PreferenceKey(op, PathType::User), defaultPath);
if (path.empty())
path = FileNames::FindDefaultPath(op);
auto result = function(path);
FileNames::UpdateDefaultPath(op, ::wxPathOnly(result));
return result;
}
// Useful functions for working with search paths
FILES_API void AddUniquePathToPathList(const FilePath &path,
FilePaths &pathList);
FILES_API void AddMultiPathsToPathList(const wxString &multiPathString,
FilePaths &pathList);
FILES_API void FindFilesInPathList(const wxString & pattern,
const FilePaths & pathList,
FilePaths &results,
int flags = wxDIR_FILES);
//! Check location on writable access and return true if checked successfully.
FILES_API bool WritableLocationCheck(const FilePath& path);
// wxString compare function for sorting case, which is needed to load correctly.
FILES_API int CompareNoCase(const wxString& first, const wxString& second);
// Create a unique filename using the passed prefix and suffix
FILES_API wxString CreateUniqueName(const wxString &prefix,
const wxString &suffix = wxEmptyString);
// File extension used for unsaved/temporary project files
FILES_API wxString UnsavedProjectExtension();
FILES_API
bool IsOnFATFileSystem(const FilePath &path);
FILES_API
//! Give enough of the path to identify the device. (On Windows, drive letter plus ':')
wxString AbbreviatePath(const wxFileName &fileName);
};
#endif

View File

@@ -0,0 +1,46 @@
/**********************************************************************
Audacity: A Digital Audio Editor
PlatformCompatibility.cpp
Markus Meyer
*******************************************************************//*!
\class PlatformCompatibility
\brief Filename Compatibility utilities.
\see FileNames
*//*******************************************************************/
#include "PlatformCompatibility.h"
#include <wx/filefn.h>
#include <wx/filename.h>
#include <wx/stdpaths.h>
#include <wx/app.h>
FilePath PlatformCompatibility::GetLongFileName(const FilePath &shortFileName)
{
wxFileName fn(shortFileName);
return fn.GetLongPath();
}
const FilePath &PlatformCompatibility::GetExecutablePath()
{
static bool found = false;
static FilePath path;
if (!found) {
path = wxStandardPaths::Get().GetExecutablePath();
found = true;
}
return path;
}

View File

@@ -0,0 +1,44 @@
/**********************************************************************
Audacity: A Digital Audio Editor
PlatformCompatibility.h
Platform-specific compatibility functions
This file implements functions needed to work around
platform-specific problems and which cannot be solved by a simple
#ifdef/#endif plus two or three lines additional code. Wherever
possible, the implementation should be such, that the function is
implemented on every platform, but is a dummy for those platforms
on which it is not needed, so additional #ifdef's are unnecessary.
Markus Meyer
**********************************************************************/
#ifndef __AUDACITY_COMPATIBILITY__
#define __AUDACITY_COMPATIBILITY__
#include "Identifier.h"
class FILES_API PlatformCompatibility
{
public:
//
// On Win32, this function gets the long file name (like
// "C:\Program Files\Project.aup3") from a short file name like
// "C:\PROGRA~1\PROJEC~1.AUP. On other systems, the function
// just returns the exact string it is given.
//
static FilePath GetLongFileName(const FilePath& shortFileName);
//
// Get filename and path of executable (e.g. "/usr/bin/audacity" on
// Linux or "C:\Program Files\Audacity\Audacity.exe" on Windows)
// This string is unchanging
//
static const FilePath &GetExecutablePath();
};
#endif

View File

@@ -0,0 +1,130 @@
/**********************************************************************
Audacity: A Digital Audio Editor
TempDirectory.cpp
Paul Licameli split from FileNames.cpp
**********************************************************************/
#include "TempDirectory.h"
#include "FileNames.h"
#include "BasicUI.h"
static wxString &TempDirPath()
{
static wxString path;
return path;
}
/// 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 TempDirectory::TempDir()
{
auto &path = TempDirPath();
if (gPrefs && path.empty())
path =
gPrefs->Read(PreferenceKey(FileNames::Operation::Temp,
FileNames::PathType::_None), wxT(""));
if (FileNames::IsOnFATFileSystem(path))
{
BasicUI::ShowErrorDialog( {},
XO("Unsuitable"),
XO("The temporary files directory is on a FAT formatted drive.\n"
"Resetting to default location."),
"Error:_Unsuitable_drive"
);
path = DefaultTempDir();
UpdateDefaultPath(FileNames::Operation::Temp, path);
}
return FileNames::MkDir(path);
}
void TempDirectory::ResetTempDir()
{
TempDirPath().clear();
}
/** \brief Default temp directory */
static FilePath sDefaultTempDir;
const FilePath &TempDirectory::DefaultTempDir()
{
return sDefaultTempDir;
}
void TempDirectory::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 TempDirectory::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
if (FATFilesystemDenied(NameCanonical,
XO("The temporary files directory is on a FAT formatted drive.\n"
"Resetting to default location.")))
{
return false;
}
return !(NameCanonical.StartsWith( BadPath ));
}
wxString TempDirectory::UnsavedProjectFileName()
{
wxFileName fn(TempDir(),
FileNames::CreateUniqueName(wxT("New Project"), FileNames::UnsavedProjectExtension()));
return fn.GetFullPath();
}
bool TempDirectory::FATFilesystemDenied( const FilePath &path,
const TranslatableString &msg, const BasicUI::WindowPlacement &placement )
{
if (FileNames::IsOnFATFileSystem(path))
{
BasicUI::ShowErrorDialog( placement,
XO("Unsuitable"),
XO("%s\n\nFor tips on suitable drives, click the help button.").Format(msg),
"Error:_Unsuitable_drive"
);
return true;
}
return false;
}

View File

@@ -0,0 +1,39 @@
/**********************************************************************
Audacity: A Digital Audio Editor
TempDirectory.h
Paul Licameli split from FileNames.h
**********************************************************************/
#ifndef __AUDACITY_TEMP_DIRECTORY__
#define __AUDACITY_TEMP_DIRECTORY__
#include "BasicUI.h"
class TranslatableString;
class wxWindow;
#include "Identifier.h"
namespace TempDirectory
{
FILES_API wxString TempDir();
FILES_API void ResetTempDir();
FILES_API const FilePath &DefaultTempDir();
FILES_API void SetDefaultTempDir( const FilePath &tempDir );
FILES_API bool IsTempDirectoryNameOK( const FilePath & Name );
// Create a filename for an unsaved/temporary project file
FILES_API wxString UnsavedProjectFileName();
FILES_API bool FATFilesystemDenied(const FilePath &path,
const TranslatableString &msg,
const BasicUI::WindowPlacement &placement = {});
};
#endif

View File

@@ -0,0 +1,92 @@
/**********************************************************************
Audacity: A Digital Audio Editor
wxFileNameWrapper.h
Paul Licameli
**********************************************************************/
#ifndef __AUDACITY_WXFILENAMEWRAPPER__
#define __AUDACITY_WXFILENAMEWRAPPER__
class wxArrayString;
#include <wx/filename.h> // to inherit
// The wxFileName does not have a move constructor.
// So add one to it, so that it passes around by value more quickly.
class wxFileNameWrapper : public wxFileName
{
public:
using wxFileName::wxFileName;
explicit
wxFileNameWrapper(const wxFileName &that)
: wxFileName(that)
{}
wxFileNameWrapper() = default;
wxFileNameWrapper(const wxFileNameWrapper &that) = default;
wxFileNameWrapper &operator= (const wxFileNameWrapper &that) = default;
void swap(wxFileNameWrapper &that)
{
if (this != &that) {
#if 0
// Awful hack number 1 makes gcc 5 choke
enum : size_t { Size = sizeof(*this) };
// Do it bitwise.
// std::aligned_storage<Size>::type buffer;
char buffer[Size];
memcpy(&buffer, this, Size);
memcpy(this, &that, Size);
memcpy(&that, &buffer, Size);
#else
// Awful hack number 2 relies on knowing the class layout
// This is the less evil one but watch out for redefinition of the base class
struct Contents
{
void swap(Contents &that) {
m_volume.swap(that.m_volume);
m_dirs.swap(that.m_dirs);
m_name.swap(that.m_name);
m_ext.swap(that.m_ext);
std::swap(m_relative, that.m_relative);
std::swap(m_hasExt, that.m_hasExt);
std::swap(m_dontFollowLinks, that.m_dontFollowLinks);
};
wxString m_volume;
wxArrayString m_dirs;
wxString m_name, m_ext;
bool m_relative;
bool m_hasExt;
bool m_dontFollowLinks;
};
reinterpret_cast<Contents*>(this)->swap
(*reinterpret_cast<Contents*>(&that));
#endif
}
}
// Define move copy and assignment in terms of swap
wxFileNameWrapper(wxFileNameWrapper &&that)
{
swap(that);
}
wxFileNameWrapper &operator= (wxFileNameWrapper &&that)
{
if (this != &that) {
Clear();
swap(that);
}
return *this;
}
};
#endif