mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-07 15:22:34 +02:00
In r13510 I had neglected to build with all local libraries and some of them needed attention. So, I also took the opportunity to work out the locale directory and how to keep it unmolested as well. As a result, all locales are rebuilt as expected, but into the "build" directory. As a bonus you may now test Audacity from the "build" directory and have Nyquist plugins and message catalogs available (so you can test other languages again without havint to install). So, again: mkdir buildme cd buildme ../configure make ./audacity
2071 lines
63 KiB
C++
2071 lines
63 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
AudacityApp.cpp
|
|
|
|
Dominic Mazzoni
|
|
|
|
******************************************************************//**
|
|
|
|
\class AudacityApp
|
|
\brief AudacityApp is the 'main' class for Audacity
|
|
|
|
It handles initialization and termination by subclassing wxApp.
|
|
|
|
*//*******************************************************************/
|
|
|
|
#if 0
|
|
// This may be used to debug memory leaks.
|
|
// See: Visual Leak Dectector @ http://dmoulding.googlepages.com/vld
|
|
#include <vld.h>
|
|
#endif
|
|
|
|
#include "Audacity.h" // This should always be included first
|
|
|
|
#include <wx/defs.h>
|
|
#include <wx/app.h>
|
|
#include <wx/bitmap.h>
|
|
#include <wx/docview.h>
|
|
#include <wx/event.h>
|
|
#include <wx/ipc.h>
|
|
#include <wx/log.h>
|
|
#include <wx/window.h>
|
|
#include <wx/intl.h>
|
|
#include <wx/menu.h>
|
|
#include <wx/msgdlg.h>
|
|
#include <wx/snglinst.h>
|
|
#include <wx/splash.h>
|
|
#include <wx/sysopt.h>
|
|
#include <wx/fontmap.h>
|
|
|
|
#include <wx/fs_zip.h>
|
|
#include <wx/image.h>
|
|
|
|
#include <wx/dir.h>
|
|
#include <wx/file.h>
|
|
#include <wx/filename.h>
|
|
|
|
#ifdef __WXGTK__
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
// chmod, lstat, geteuid
|
|
#ifdef __UNIX__
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#include "AudacityApp.h"
|
|
|
|
#include "AudacityLogger.h"
|
|
#include "AboutDialog.h"
|
|
#include "AColor.h"
|
|
#include "AudioIO.h"
|
|
#include "Benchmark.h"
|
|
#include "DirManager.h"
|
|
#include "commands/CommandHandler.h"
|
|
#include "commands/AppCommandEvent.h"
|
|
#include "effects/LoadEffects.h"
|
|
#include "effects/Contrast.h"
|
|
#include "widgets/ASlider.h"
|
|
#include "FFmpeg.h"
|
|
#include "Internat.h"
|
|
#include "LangChoice.h"
|
|
#include "Languages.h"
|
|
#include "PluginManager.h"
|
|
#include "Prefs.h"
|
|
#include "Project.h"
|
|
#include "Screenshot.h"
|
|
#include "Sequence.h"
|
|
#include "WaveTrack.h"
|
|
#include "Internat.h"
|
|
#include "prefs/PrefsDialog.h"
|
|
#include "Theme.h"
|
|
#include "PlatformCompatibility.h"
|
|
#include "FileNames.h"
|
|
#include "AutoRecovery.h"
|
|
#include "SplashDialog.h"
|
|
#include "FFT.h"
|
|
#include "BlockFile.h"
|
|
#include "ondemand/ODManager.h"
|
|
#include "commands/Keyboard.h"
|
|
#include "widgets/ErrorDialog.h"
|
|
|
|
//temporarilly commented out till it is added to all projects
|
|
//#include "Profiler.h"
|
|
|
|
#include "ModuleManager.h"
|
|
|
|
#include "import/Import.h"
|
|
|
|
#ifdef EXPERIMENTAL_SCOREALIGN
|
|
#include "effects/ScoreAlignDialog.h"
|
|
#endif
|
|
|
|
#if 0
|
|
#ifdef _DEBUG
|
|
#ifdef _MSC_VER
|
|
#undef THIS_FILE
|
|
static char*THIS_FILE= __FILE__;
|
|
#define new new(_NORMAL_BLOCK, THIS_FILE, __LINE__)
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
// Windows specific linker control...only needed once so
|
|
// this is a good place (unless we want to add another file).
|
|
#if defined(__WXMSW__)
|
|
|
|
// These lines ensure that Audacity gets WindowsXP themes.
|
|
// Without them we get the old-style Windows98/2000 look under XP.
|
|
# if !defined(__WXWINCE__)
|
|
# pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df'\"")
|
|
# endif
|
|
|
|
// These lines allows conditional inclusion of the various libraries
|
|
// that Audacity can use.
|
|
|
|
# if defined(USE_LIBFLAC)
|
|
# pragma comment(lib, "libflac++")
|
|
# pragma comment(lib, "libflac")
|
|
# endif
|
|
|
|
# if defined(USE_LIBID3TAG)
|
|
# pragma comment(lib, "libid3tag")
|
|
# endif
|
|
|
|
# if defined(USE_LIBMAD)
|
|
# pragma comment(lib, "libmad")
|
|
# endif
|
|
|
|
# if defined(USE_LIBRESAMPLE)
|
|
# pragma comment(lib, "libresample")
|
|
# endif
|
|
|
|
# if defined(USE_LIBSAMPLERATE)
|
|
# pragma comment(lib, "libsamplerate")
|
|
# endif
|
|
|
|
# if defined(USE_LIBSOXR)
|
|
# pragma comment(lib, "libsoxr")
|
|
# endif
|
|
|
|
# if defined(USE_LIBTWOLAME)
|
|
# pragma comment(lib, "twolame")
|
|
# endif
|
|
|
|
# if defined(USE_LIBVORBIS)
|
|
# pragma comment(lib, "libogg")
|
|
# pragma comment(lib, "libvorbis")
|
|
# endif
|
|
|
|
# if defined(USE_LV2)
|
|
# pragma comment(lib, "lv2")
|
|
# endif
|
|
|
|
# if defined(USE_MIDI)
|
|
# pragma comment(lib, "portsmf")
|
|
# endif
|
|
|
|
# if defined(EXPERIMENTAL_MIDI_OUT)
|
|
# pragma comment(lib, "portmidi")
|
|
# endif
|
|
|
|
# if defined(EXPERIMENTAL_SCOREALIGN)
|
|
# pragma comment(lib, "libscorealign")
|
|
# endif
|
|
|
|
# if defined(USE_NYQUIST)
|
|
# pragma comment(lib, "libnyquist")
|
|
# endif
|
|
|
|
# if defined(USE_PORTMIXER)
|
|
# pragma comment(lib, "portmixer")
|
|
# endif
|
|
|
|
# if defined(USE_SBSMS)
|
|
# pragma comment(lib, "sbsms")
|
|
# endif
|
|
|
|
# if defined(USE_SOUNDTOUCH)
|
|
# pragma comment(lib, "soundtouch")
|
|
# endif
|
|
|
|
# if defined(USE_VAMP)
|
|
# pragma comment(lib, "libvamp")
|
|
# endif
|
|
|
|
#endif //(__WXMSW__)
|
|
|
|
#include "../images/AudacityLogoWithName.xpm"
|
|
|
|
////////////////////////////////////////////////////////////
|
|
/// Custom events
|
|
////////////////////////////////////////////////////////////
|
|
|
|
DEFINE_EVENT_TYPE(EVT_OPEN_AUDIO_FILE);
|
|
DEFINE_EVENT_TYPE(EVT_CAPTURE_KEYBOARD);
|
|
DEFINE_EVENT_TYPE(EVT_RELEASE_KEYBOARD);
|
|
DEFINE_EVENT_TYPE(EVT_CAPTURE_KEY);
|
|
|
|
#ifdef __WXGTK__
|
|
static void wxOnAssert(const wxChar *fileName, int lineNumber, const wxChar *msg)
|
|
{
|
|
if (msg)
|
|
printf("ASSERTION FAILED: %s\n%s: %d\n", (const char *)wxString(msg).mb_str(), (const char *)wxString(fileName).mb_str(), lineNumber);
|
|
else
|
|
printf("ASSERTION FAILED!\n%s: %d\n", (const char *)wxString(fileName).mb_str(), lineNumber);
|
|
|
|
// Force core dump
|
|
int *i = 0;
|
|
if (*i)
|
|
exit(1);
|
|
|
|
exit(0);
|
|
}
|
|
#endif
|
|
|
|
static wxFrame *gParentFrame = NULL;
|
|
|
|
static bool gInited = false;
|
|
bool gIsQuitting = false;
|
|
|
|
void QuitAudacity(bool bForce)
|
|
{
|
|
if (gIsQuitting)
|
|
return;
|
|
|
|
gIsQuitting = true;
|
|
|
|
// Try to close each open window. If the user hits Cancel
|
|
// in a Save Changes dialog, don't continue.
|
|
// BG: unless force is true
|
|
|
|
// BG: Are there any projects open?
|
|
//- if (!gAudacityProjects.IsEmpty())
|
|
/*start+*/
|
|
if (gAudacityProjects.IsEmpty())
|
|
{
|
|
#ifdef __WXMAC__
|
|
AudacityProject::DeleteClipboard();
|
|
#endif
|
|
}
|
|
else
|
|
/*end+*/
|
|
{
|
|
SaveWindowSize();
|
|
while (gAudacityProjects.Count())
|
|
{
|
|
if (bForce)
|
|
{
|
|
gAudacityProjects[0]->Close(true);
|
|
}
|
|
else
|
|
{
|
|
if (!gAudacityProjects[0]->Close())
|
|
{
|
|
gIsQuitting = false;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LWSlider::DeleteSharedTipPanel();
|
|
|
|
ModuleManager::Get().Dispatch(AppQuiting);
|
|
|
|
if (gParentFrame)
|
|
gParentFrame->Destroy();
|
|
gParentFrame = NULL;
|
|
|
|
CloseContrastDialog();
|
|
#ifdef EXPERIMENTAL_SCOREALIGN
|
|
CloseScoreAlignDialog();
|
|
#endif
|
|
CloseScreenshotTools();
|
|
|
|
//release ODManager Threads
|
|
ODManager::Quit();
|
|
|
|
//print out profile if we have one by deleting it
|
|
//temporarilly commented out till it is added to all projects
|
|
//delete Profiler::Instance();
|
|
|
|
//delete the static lock for audacity projects
|
|
AudacityProject::DeleteAllProjectsDeleteLock();
|
|
|
|
//remove our logger
|
|
delete wxLog::SetActiveTarget(NULL);
|
|
|
|
if (bForce)
|
|
{
|
|
wxExit();
|
|
}
|
|
}
|
|
|
|
void QuitAudacity()
|
|
{
|
|
QuitAudacity(false);
|
|
}
|
|
|
|
void SaveWindowSize()
|
|
{
|
|
if (wxGetApp().GetWindowRectAlreadySaved())
|
|
{
|
|
return;
|
|
}
|
|
bool validWindowForSaveWindowSize = FALSE;
|
|
AudacityProject * validProject = NULL;
|
|
bool foundIconizedProject = FALSE;
|
|
size_t numProjects = gAudacityProjects.Count();
|
|
for (size_t i = 0; i < numProjects; i++)
|
|
{
|
|
if (!gAudacityProjects[i]->IsIconized()) {
|
|
validWindowForSaveWindowSize = TRUE;
|
|
validProject = gAudacityProjects[i];
|
|
i = numProjects;
|
|
}
|
|
else
|
|
foundIconizedProject = TRUE;
|
|
|
|
}
|
|
if (validWindowForSaveWindowSize)
|
|
{
|
|
wxRect windowRect = validProject->GetRect();
|
|
wxRect normalRect = validProject->GetNormalizedWindowState();
|
|
bool wndMaximized = validProject->IsMaximized();
|
|
gPrefs->Write(wxT("/Window/X"), windowRect.GetX());
|
|
gPrefs->Write(wxT("/Window/Y"), windowRect.GetY());
|
|
gPrefs->Write(wxT("/Window/Width"), windowRect.GetWidth());
|
|
gPrefs->Write(wxT("/Window/Height"), windowRect.GetHeight());
|
|
gPrefs->Write(wxT("/Window/Maximized"), wndMaximized);
|
|
gPrefs->Write(wxT("/Window/Normal_X"), normalRect.GetX());
|
|
gPrefs->Write(wxT("/Window/Normal_Y"), normalRect.GetY());
|
|
gPrefs->Write(wxT("/Window/Normal_Width"), normalRect.GetWidth());
|
|
gPrefs->Write(wxT("/Window/Normal_Height"), normalRect.GetHeight());
|
|
gPrefs->Write(wxT("/Window/Iconized"), FALSE);
|
|
}
|
|
else
|
|
{
|
|
if (foundIconizedProject) {
|
|
validProject = gAudacityProjects[0];
|
|
bool wndMaximized = validProject->IsMaximized();
|
|
wxRect normalRect = validProject->GetNormalizedWindowState();
|
|
// store only the normal rectangle because the itemized rectangle
|
|
// makes no sense for an opening project window
|
|
gPrefs->Write(wxT("/Window/X"), normalRect.GetX());
|
|
gPrefs->Write(wxT("/Window/Y"), normalRect.GetY());
|
|
gPrefs->Write(wxT("/Window/Width"), normalRect.GetWidth());
|
|
gPrefs->Write(wxT("/Window/Height"), normalRect.GetHeight());
|
|
gPrefs->Write(wxT("/Window/Maximized"), wndMaximized);
|
|
gPrefs->Write(wxT("/Window/Normal_X"), normalRect.GetX());
|
|
gPrefs->Write(wxT("/Window/Normal_Y"), normalRect.GetY());
|
|
gPrefs->Write(wxT("/Window/Normal_Width"), normalRect.GetWidth());
|
|
gPrefs->Write(wxT("/Window/Normal_Height"), normalRect.GetHeight());
|
|
gPrefs->Write(wxT("/Window/Iconized"), TRUE);
|
|
}
|
|
else {
|
|
// this would be a very strange case that might possibly occur on the Mac
|
|
// Audacity would have to be running with no projects open
|
|
// in this case we are going to write only the default values
|
|
wxRect defWndRect;
|
|
GetDefaultWindowRect(&defWndRect);
|
|
gPrefs->Write(wxT("/Window/X"), defWndRect.GetX());
|
|
gPrefs->Write(wxT("/Window/Y"), defWndRect.GetY());
|
|
gPrefs->Write(wxT("/Window/Width"), defWndRect.GetWidth());
|
|
gPrefs->Write(wxT("/Window/Height"), defWndRect.GetHeight());
|
|
gPrefs->Write(wxT("/Window/Maximized"), FALSE);
|
|
gPrefs->Write(wxT("/Window/Normal_X"), defWndRect.GetX());
|
|
gPrefs->Write(wxT("/Window/Normal_Y"), defWndRect.GetY());
|
|
gPrefs->Write(wxT("/Window/Normal_Width"), defWndRect.GetWidth());
|
|
gPrefs->Write(wxT("/Window/Normal_Height"), defWndRect.GetHeight());
|
|
gPrefs->Write(wxT("/Window/Iconized"), FALSE);
|
|
}
|
|
}
|
|
gPrefs->Flush();
|
|
wxGetApp().SetWindowRectAlreadySaved(TRUE);
|
|
}
|
|
|
|
#if defined(__WXGTK__) && defined(HAVE_GTK)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Provide the ability to receive notification from the session manager
|
|
// when the user is logging out or shutting down.
|
|
//
|
|
// Most of this was taken from nsNativeAppSupportUnix.cpp from Mozilla.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// TODO: May need updating. Is this code too obsolete (relying on Gnome2 so's) to be
|
|
// worth keeping anymore?
|
|
// CB suggests we use libSM directly ref:
|
|
// http://www.x.org/archive/X11R7.7/doc/libSM/SMlib.html#The_Save_Yourself_Callback
|
|
|
|
#include <dlfcn.h>
|
|
/* There is a conflict between the type names used in Glib >= 2.21 and those in
|
|
* wxGTK (http://trac.wxwidgets.org/ticket/10883)
|
|
* Happily we can avoid the hack, as we only need some of the headers, not
|
|
* the full GTK headers
|
|
*/
|
|
#include <glib-object.h>
|
|
|
|
typedef struct _GnomeProgram GnomeProgram;
|
|
typedef struct _GnomeModuleInfo GnomeModuleInfo;
|
|
typedef struct _GnomeClient GnomeClient;
|
|
|
|
typedef enum
|
|
{
|
|
GNOME_SAVE_GLOBAL,
|
|
GNOME_SAVE_LOCAL,
|
|
GNOME_SAVE_BOTH
|
|
} GnomeSaveStyle;
|
|
|
|
typedef enum
|
|
{
|
|
GNOME_INTERACT_NONE,
|
|
GNOME_INTERACT_ERRORS,
|
|
GNOME_INTERACT_ANY
|
|
} GnomeInteractStyle;
|
|
|
|
typedef enum
|
|
{
|
|
GNOME_DIALOG_ERROR,
|
|
GNOME_DIALOG_NORMAL
|
|
} GnomeDialogType;
|
|
|
|
typedef GnomeProgram * (*_gnome_program_init_fn)(const char *,
|
|
const char *,
|
|
const GnomeModuleInfo *,
|
|
int,
|
|
char **,
|
|
const char *,
|
|
...);
|
|
typedef const GnomeModuleInfo * (*_libgnomeui_module_info_get_fn)();
|
|
typedef GnomeClient * (*_gnome_master_client_fn)(void);
|
|
typedef void (*GnomeInteractFunction)(GnomeClient *,
|
|
gint,
|
|
GnomeDialogType,
|
|
gpointer);
|
|
typedef void (*_gnome_client_request_interaction_fn)(GnomeClient *,
|
|
GnomeDialogType,
|
|
GnomeInteractFunction,
|
|
gpointer);
|
|
typedef void (*_gnome_interaction_key_return_fn)(gint, gboolean);
|
|
|
|
static _gnome_client_request_interaction_fn gnome_client_request_interaction;
|
|
static _gnome_interaction_key_return_fn gnome_interaction_key_return;
|
|
|
|
static void interact_cb(GnomeClient *client,
|
|
gint key,
|
|
GnomeDialogType type,
|
|
gpointer data)
|
|
{
|
|
wxCloseEvent e(wxEVT_QUERY_END_SESSION, wxID_ANY);
|
|
e.SetEventObject(&wxGetApp());
|
|
e.SetCanVeto(true);
|
|
|
|
wxGetApp().ProcessEvent(e);
|
|
|
|
gnome_interaction_key_return(key, e.GetVeto());
|
|
}
|
|
|
|
static gboolean save_yourself_cb(GnomeClient *client,
|
|
gint phase,
|
|
GnomeSaveStyle style,
|
|
gboolean shutdown,
|
|
GnomeInteractStyle interact,
|
|
gboolean fast,
|
|
gpointer user_data)
|
|
{
|
|
if (!shutdown || interact != GNOME_INTERACT_ANY) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (gAudacityProjects.IsEmpty()) {
|
|
return TRUE;
|
|
}
|
|
|
|
gnome_client_request_interaction(client,
|
|
GNOME_DIALOG_NORMAL,
|
|
interact_cb,
|
|
NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
class GnomeShutdown
|
|
{
|
|
public:
|
|
GnomeShutdown()
|
|
{
|
|
mArgv[0] = strdup("Audacity");
|
|
|
|
mGnomeui = dlopen("libgnomeui-2.so.0", RTLD_NOW);
|
|
if (!mGnomeui) {
|
|
return;
|
|
}
|
|
|
|
mGnome = dlopen("libgnome-2.so.0", RTLD_NOW);
|
|
if (!mGnome) {
|
|
return;
|
|
}
|
|
|
|
_gnome_program_init_fn gnome_program_init = (_gnome_program_init_fn)
|
|
dlsym(mGnome, "gnome_program_init");
|
|
_libgnomeui_module_info_get_fn libgnomeui_module_info_get = (_libgnomeui_module_info_get_fn)
|
|
dlsym(mGnomeui, "libgnomeui_module_info_get");
|
|
_gnome_master_client_fn gnome_master_client = (_gnome_master_client_fn)
|
|
dlsym(mGnomeui, "gnome_master_client");
|
|
|
|
gnome_client_request_interaction = (_gnome_client_request_interaction_fn)
|
|
dlsym(mGnomeui, "gnome_client_request_interaction");
|
|
gnome_interaction_key_return = (_gnome_interaction_key_return_fn)
|
|
dlsym(mGnomeui, "gnome_interaction_key_return");
|
|
|
|
|
|
if (!gnome_program_init || !libgnomeui_module_info_get) {
|
|
return;
|
|
}
|
|
|
|
gnome_program_init(mArgv[0],
|
|
"1.0",
|
|
libgnomeui_module_info_get(),
|
|
1,
|
|
mArgv,
|
|
NULL);
|
|
|
|
mClient = gnome_master_client();
|
|
if (mClient == NULL) {
|
|
return;
|
|
}
|
|
|
|
g_signal_connect(mClient, "save-yourself", G_CALLBACK(save_yourself_cb), NULL);
|
|
}
|
|
|
|
virtual ~GnomeShutdown()
|
|
{
|
|
// Do not dlclose() the libraries here lest you want segfaults...
|
|
|
|
free(mArgv[0]);
|
|
}
|
|
|
|
private:
|
|
|
|
char *mArgv[1];
|
|
void *mGnomeui;
|
|
void *mGnome;
|
|
GnomeClient *mClient;
|
|
};
|
|
|
|
// This variable exists to call the constructor and
|
|
// connect a signal for the 'save-yourself' message.
|
|
GnomeShutdown GnomeShutdownInstance;
|
|
|
|
#endif
|
|
|
|
#if defined(__WXMSW__)
|
|
|
|
//
|
|
// DDE support for opening multiple files with one instance
|
|
// of Audacity.
|
|
//
|
|
|
|
#define IPC_APPL wxT("audacity")
|
|
#define IPC_TOPIC wxT("System")
|
|
|
|
class IPCConn : public wxConnection
|
|
{
|
|
public:
|
|
IPCConn()
|
|
: wxConnection()
|
|
{
|
|
};
|
|
|
|
~IPCConn()
|
|
{
|
|
};
|
|
|
|
bool OnExecute(const wxString & WXUNUSED(topic),
|
|
wxChar *data,
|
|
int WXUNUSED(size),
|
|
wxIPCFormat WXUNUSED(format))
|
|
{
|
|
if (!gInited) {
|
|
return false;
|
|
}
|
|
|
|
AudacityProject *project = CreateNewAudacityProject();
|
|
|
|
wxString cmd(data);
|
|
|
|
// We queue a command event to the project responsible for
|
|
// opening the file since it can be a long process and we
|
|
// only have 5 seconds to return the Execute message to the
|
|
// client.
|
|
if (!cmd.IsEmpty()) {
|
|
wxCommandEvent e(EVT_OPEN_AUDIO_FILE);
|
|
e.SetString(data);
|
|
project->GetEventHandler()->AddPendingEvent(e);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
};
|
|
|
|
class IPCServ : public wxServer
|
|
{
|
|
public:
|
|
IPCServ()
|
|
: wxServer()
|
|
{
|
|
Create(IPC_APPL);
|
|
};
|
|
|
|
~IPCServ()
|
|
{
|
|
};
|
|
|
|
wxConnectionBase *OnAcceptConnection(const wxString & topic)
|
|
{
|
|
if (topic != IPC_TOPIC) {
|
|
return NULL;
|
|
}
|
|
|
|
return new IPCConn();
|
|
};
|
|
};
|
|
|
|
#endif //__WXMSW__
|
|
|
|
#ifndef __WXMAC__
|
|
IMPLEMENT_APP(AudacityApp)
|
|
/* make the application class known to wxWidgets for dynamic construction */
|
|
#endif
|
|
|
|
#ifdef __WXMAC__
|
|
// This should be removed when Lame and FFmpeg support is converted
|
|
// from loadable libraries to commands.mLogger
|
|
//
|
|
// The purpose of this is to give the user more control over where libraries
|
|
// such as Lame and FFmpeg get loaded from.
|
|
//
|
|
// Since absolute pathnames are used when loading these libraries, the normal search
|
|
// path would be DYLD_LIBRARY_PATH, absolute path, DYLD_FALLBACK_LIBRARY_PATH. This
|
|
// means that DYLD_LIBRARY_PATH can override what the user actually wants.
|
|
//
|
|
// So, we simply clear DYLD_LIBRARY_PATH to allow the users choice to be the first
|
|
// one tried.
|
|
IMPLEMENT_APP_NO_MAIN(AudacityApp)
|
|
IMPLEMENT_WX_THEME_SUPPORT
|
|
int main(int argc, char *argv[])
|
|
{
|
|
if (getenv("DYLD_LIBRARY_PATH")) {
|
|
extern char **environ;
|
|
|
|
unsetenv("DYLD_LIBRARY_PATH");
|
|
execve(argv[0], argv, environ);
|
|
}
|
|
return wxEntry(argc, argv);
|
|
}
|
|
#endif
|
|
|
|
#ifdef __WXMAC__
|
|
|
|
// Where drag/drop or "Open With" filenames get stored until
|
|
// the timer routine gets around to picking them up.
|
|
static wxArrayString ofqueue;
|
|
|
|
// in response of an open-document apple event
|
|
void AudacityApp::MacOpenFile(const wxString &fileName)
|
|
{
|
|
ofqueue.Add(fileName);
|
|
}
|
|
|
|
// in response of a print-document apple event
|
|
void AudacityApp::MacPrintFile(const wxString &fileName)
|
|
{
|
|
ofqueue.Add(fileName);
|
|
}
|
|
|
|
// in response of a open-application apple event
|
|
void AudacityApp::MacNewFile()
|
|
{
|
|
if (!gInited)
|
|
return;
|
|
|
|
// This method should only be used on the Mac platform
|
|
// when no project windows are open.
|
|
|
|
if (gAudacityProjects.GetCount() == 0) {
|
|
CreateNewAudacityProject();
|
|
}
|
|
}
|
|
|
|
#endif //__WXMAC__
|
|
|
|
typedef int (AudacityApp::*SPECIALKEYEVENT)(wxKeyEvent&);
|
|
|
|
#define ID_RECENT_CLEAR 6100
|
|
#define ID_RECENT_FIRST 6101
|
|
#define ID_RECENT_LAST 6112
|
|
|
|
// we don't really care about the timer id, but set this value just in case we do in the future
|
|
#define kAudacityAppTimerID 0
|
|
|
|
BEGIN_EVENT_TABLE(AudacityApp, wxApp)
|
|
EVT_QUERY_END_SESSION(AudacityApp::OnEndSession)
|
|
|
|
EVT_KEY_DOWN(AudacityApp::OnKeyDown)
|
|
EVT_CHAR(AudacityApp::OnChar)
|
|
EVT_KEY_UP(AudacityApp::OnKeyUp)
|
|
EVT_TIMER(kAudacityAppTimerID, AudacityApp::OnTimer)
|
|
#ifdef __WXMAC__
|
|
EVT_MENU(wxID_NEW, AudacityApp::OnMenuNew)
|
|
EVT_MENU(wxID_OPEN, AudacityApp::OnMenuOpen)
|
|
EVT_MENU(wxID_ABOUT, AudacityApp::OnMenuAbout)
|
|
EVT_MENU(wxID_PREFERENCES, AudacityApp::OnMenuPreferences)
|
|
EVT_MENU(wxID_EXIT, AudacityApp::OnMenuExit)
|
|
#endif
|
|
// Recent file event handlers.
|
|
EVT_MENU(ID_RECENT_CLEAR, AudacityApp::OnMRUClear)
|
|
EVT_MENU_RANGE(ID_RECENT_FIRST, ID_RECENT_LAST, AudacityApp::OnMRUFile)
|
|
|
|
// Handle AppCommandEvents (usually from a script)
|
|
EVT_APP_COMMAND(wxID_ANY, AudacityApp::OnReceiveCommand)
|
|
END_EVENT_TABLE()
|
|
|
|
// backend for OnMRUFile
|
|
// TODO: Would be nice to make this handle not opening a file with more panache.
|
|
// - Inform the user if DefaultOpenPath not set.
|
|
// - Switch focus to correct instance of project window, if already open.
|
|
bool AudacityApp::MRUOpen(wxString fullPathStr) {
|
|
// Most of the checks below are copied from AudacityProject::OpenFiles.
|
|
// - some rationalisation might be possible.
|
|
|
|
AudacityProject *proj = GetActiveProject();
|
|
|
|
if (!fullPathStr.IsEmpty())
|
|
{
|
|
// verify that the file exists
|
|
if (wxFile::Exists(fullPathStr))
|
|
{
|
|
if (!gPrefs->Write(wxT("/DefaultOpenPath"), wxPathOnly(fullPathStr)) ||
|
|
!gPrefs->Flush())
|
|
return false;
|
|
|
|
// Make sure it isn't already open.
|
|
// Test here even though AudacityProject::OpenFile() also now checks, because
|
|
// that method does not return the bad result.
|
|
// That itself may be a FIXME.
|
|
if (AudacityProject::IsAlreadyOpen(fullPathStr))
|
|
return false;
|
|
|
|
// DMM: If the project is dirty, that means it's been touched at
|
|
// all, and it's not safe to open a new project directly in its
|
|
// place. Only if the project is brand-new clean and the user
|
|
// hasn't done any action at all is it safe for Open to take place
|
|
// inside the current project.
|
|
//
|
|
// If you try to Open a new project inside the current window when
|
|
// there are no tracks, but there's an Undo history, etc, then
|
|
// bad things can happen, including data files moving to the new
|
|
// project directory, etc.
|
|
if (!proj || proj->GetDirty() || !proj->GetIsEmpty()) {
|
|
proj = CreateNewAudacityProject();
|
|
}
|
|
// This project is clean; it's never been touched. Therefore
|
|
// all relevant member variables are in their initial state,
|
|
// and it's okay to open a new project inside this window.
|
|
proj->OpenFile(fullPathStr);
|
|
}
|
|
else {
|
|
// File doesn't exist - remove file from history
|
|
wxMessageBox(wxString::Format(_("%s could not be found.\n\nIt has been removed from the list of recent files."),
|
|
fullPathStr.c_str()));
|
|
return(false);
|
|
}
|
|
}
|
|
return(true);
|
|
}
|
|
|
|
void AudacityApp::OnMRUClear(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
mRecentFiles->Clear();
|
|
}
|
|
|
|
//vvv Basically, anything from Recent Files is treated as a .aup, until proven otherwise,
|
|
// then it tries to Import(). Very questionable handling, imo.
|
|
// Better, for example, to check the file type early on.
|
|
void AudacityApp::OnMRUFile(wxCommandEvent& event) {
|
|
int n = event.GetId() - ID_RECENT_FIRST;
|
|
wxString fullPathStr = mRecentFiles->GetHistoryFile(n);
|
|
|
|
// Try to open only if not already open.
|
|
// Test IsAlreadyOpen() here even though AudacityProject::MRUOpen() also now checks,
|
|
// because we don't want to RemoveFileFromHistory() just because it already exists,
|
|
// and AudacityApp::OnMacOpenFile() calls MRUOpen() directly.
|
|
// that method does not return the bad result.
|
|
if (!AudacityProject::IsAlreadyOpen(fullPathStr) && !MRUOpen(fullPathStr))
|
|
mRecentFiles->RemoveFileFromHistory(n);
|
|
}
|
|
|
|
void AudacityApp::OnTimer(wxTimerEvent& WXUNUSED(event))
|
|
{
|
|
#if defined(__WXMAC__)
|
|
// Filenames are queued when Audacity receives the a few of the
|
|
// AppleEvent messages (via wxWidgets). So, open any that are
|
|
// in the queue and clean the queue.
|
|
if (ofqueue.GetCount()) {
|
|
// Load each file on the queue
|
|
while (ofqueue.GetCount()) {
|
|
wxString name(ofqueue[0]);
|
|
ofqueue.RemoveAt(0);
|
|
// TODO: Handle failures better.
|
|
// Some failures are OK, e.g. file not found, just would-be-nices to do better,
|
|
// so FAIL_MSG is more a case of an enhancement request than an actual problem.
|
|
// LL: In all but one case an appropriate message is already displayed. The
|
|
// instance that a message is NOT displayed is when a failure to write
|
|
// to the config file has occurred.
|
|
if (!MRUOpen(name)) {
|
|
wxFAIL_MSG(wxT("MRUOpen failed"));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Check if a warning for missing aliased files should be displayed
|
|
if (ShouldShowMissingAliasedFileWarning()) {
|
|
// find which project owns the blockfile
|
|
// note: there may be more than 1, but just go with the first one.
|
|
size_t numProjects = gAudacityProjects.Count();
|
|
wxString missingFileName;
|
|
AudacityProject *offendingProject = NULL;
|
|
|
|
m_LastMissingBlockFileLock.Lock();
|
|
if (numProjects == 1) {
|
|
// if there is only one project open, no need to search
|
|
offendingProject = gAudacityProjects[0];
|
|
} else if (numProjects > 1) {
|
|
for (size_t i = 0; i < numProjects; i++) {
|
|
// search each project for the blockfile
|
|
if (gAudacityProjects[i]->GetDirManager()->ContainsBlockFile(m_LastMissingBlockFile)) {
|
|
offendingProject = gAudacityProjects[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
missingFileName = ((AliasBlockFile*)m_LastMissingBlockFile)->GetAliasedFileName().GetFullPath();
|
|
m_LastMissingBlockFileLock.Unlock();
|
|
|
|
// if there are no projects open, don't show the warning (user has closed it)
|
|
if (offendingProject) {
|
|
offendingProject->Iconize(false);
|
|
offendingProject->Raise();
|
|
|
|
wxString errorMessage = wxString::Format(_(
|
|
"One or more external audio files could not be found.\n\
|
|
It is possible they were moved, deleted, or the drive they \
|
|
were on was unmounted.\n\
|
|
Silence is being substituted for the affected audio.\n\
|
|
The first detected missing file is:\n\
|
|
%s\n\
|
|
There may be additional missing files.\n\
|
|
Choose File > Check Dependencies to view a list of \
|
|
locations of the missing files."), missingFileName.c_str());
|
|
|
|
// if an old dialog exists, raise it if it is
|
|
if (offendingProject->GetMissingAliasFileDialog()) {
|
|
offendingProject->GetMissingAliasFileDialog()->Raise();
|
|
} else {
|
|
ShowAliasMissingDialog(offendingProject, _("Files Missing"),
|
|
errorMessage, wxT(""), true);
|
|
}
|
|
}
|
|
// Only show this warning once per event (playback/menu item/etc).
|
|
SetMissingAliasedFileWarningShouldShow(false);
|
|
}
|
|
}
|
|
|
|
void AudacityApp::MarkAliasedFilesMissingWarning(BlockFile *b)
|
|
{
|
|
// the reference counting provides thread safety.
|
|
if (b)
|
|
b->Ref();
|
|
|
|
m_LastMissingBlockFileLock.Lock();
|
|
if (m_LastMissingBlockFile)
|
|
m_LastMissingBlockFile->Deref();
|
|
|
|
m_LastMissingBlockFile = b;
|
|
|
|
m_LastMissingBlockFileLock.Unlock();
|
|
}
|
|
|
|
void AudacityApp::SetMissingAliasedFileWarningShouldShow(bool b)
|
|
{
|
|
// Note that this is can be called by both the main thread and other threads.
|
|
// I don't believe we need a mutex because we are checking zero vs non-zero,
|
|
// and the setting from other threads will always be non-zero (true), and the
|
|
// setting from the main thread is always false.
|
|
m_aliasMissingWarningShouldShow = b;
|
|
// reset the warnings as they were probably marked by a previous run
|
|
if (m_aliasMissingWarningShouldShow) {
|
|
MarkAliasedFilesMissingWarning(NULL);
|
|
}
|
|
}
|
|
|
|
bool AudacityApp::ShouldShowMissingAliasedFileWarning()
|
|
{
|
|
bool ret = m_LastMissingBlockFile && m_aliasMissingWarningShouldShow;
|
|
|
|
return ret;
|
|
}
|
|
|
|
AudacityLogger *AudacityApp::GetLogger()
|
|
{
|
|
return wxDynamicCast(wxLog::GetActiveTarget(), AudacityLogger);
|
|
}
|
|
|
|
void AudacityApp::InitLang( const wxString & lang )
|
|
{
|
|
if( mLocale )
|
|
delete mLocale;
|
|
|
|
// LL: I do not know why loading translations fail on the Mac if LANG is not
|
|
// set, but for some reason it does. So wrap the creation of wxLocale
|
|
// with the default translation.
|
|
//
|
|
// 2013-09-13: I've checked this again and it is still required. Still
|
|
// no idea why.
|
|
#if defined(__WXMAC__)
|
|
wxString oldval;
|
|
bool existed;
|
|
|
|
existed = wxGetEnv(wxT("LANG"), &oldval);
|
|
wxSetEnv(wxT("LANG"), wxT("en_US"));
|
|
#endif
|
|
|
|
mLocale = new wxLocale(wxT(""), lang, wxT(""), true, true);
|
|
|
|
#if defined(__WXMAC__)
|
|
if (existed) {
|
|
wxSetEnv(wxT("LANG"), oldval);
|
|
}
|
|
else {
|
|
wxUnsetEnv(wxT("LANG"));
|
|
}
|
|
#endif
|
|
|
|
for(unsigned int i=0; i<audacityPathList.GetCount(); i++)
|
|
mLocale->AddCatalogLookupPathPrefix(audacityPathList[i]);
|
|
|
|
// LL: Must add the wxWidgets catalog manually since the search
|
|
// paths were not set up when mLocale was created. The
|
|
// catalogs are search in LIFO order, so add wxstd first.
|
|
mLocale->AddCatalog(wxT("wxstd"));
|
|
|
|
// AUDACITY_NAME is legitimately used on some *nix configurations.
|
|
#ifdef AUDACITY_NAME
|
|
mLocale->AddCatalog(wxT(AUDACITY_NAME));
|
|
#else
|
|
mLocale->AddCatalog(IPC_APPL);
|
|
#endif
|
|
|
|
// Initialize internationalisation (number formats etc.)
|
|
//
|
|
// This must go _after_ creating the wxLocale instance because
|
|
// creating the wxLocale instance sets the application-wide locale.
|
|
Internat::Init();
|
|
}
|
|
|
|
// Only used when checking plugins
|
|
void AudacityApp::OnFatalException()
|
|
{
|
|
exit(-1);
|
|
}
|
|
|
|
#if defined(__WXGTK__)
|
|
// On wxGTK, there's a focus issue where dialogs do not automatically pass focus
|
|
// to the first child. This means that you can use the keyboard to navigate within
|
|
// the dialog. Watching for the ACTIVATE event allows us to set the focus ourselves
|
|
// when each dialog opens.
|
|
//
|
|
// See bug #57
|
|
//
|
|
int AudacityApp::FilterEvent(wxEvent & event)
|
|
{
|
|
if (event.GetEventType() == wxEVT_ACTIVATE)
|
|
{
|
|
wxActivateEvent & e = (wxActivateEvent &) event;
|
|
|
|
if (e.GetEventObject() && e.GetActive() && e.GetEventObject()->IsKindOf(CLASSINFO(wxDialog)))
|
|
{
|
|
((wxWindow *)e.GetEventObject())->SetFocus();
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
#endif
|
|
#include "effects/VST/VSTEffect.h"
|
|
|
|
// The `main program' equivalent, creating the windows and returning the
|
|
// main frame
|
|
bool AudacityApp::OnInit()
|
|
{
|
|
delete wxLog::SetActiveTarget(new AudacityLogger);
|
|
|
|
m_aliasMissingWarningShouldShow = true;
|
|
m_LastMissingBlockFile = NULL;
|
|
|
|
#if defined(__WXGTK__)
|
|
// Workaround for bug 154 -- initialize to false
|
|
inKbdHandler = false;
|
|
#endif
|
|
|
|
#if defined(__WXMAC__)
|
|
// Disable window animation
|
|
wxSystemOptions::SetOption( wxMAC_WINDOW_PLAIN_TRANSITION, 1 );
|
|
#endif
|
|
|
|
// LL: Moved here from InitPreferences() to ensure VST effect
|
|
// discovery writes configuration to the correct directory
|
|
// on OSX with case-sensitive file systems.
|
|
#ifdef AUDACITY_NAME
|
|
wxString appName = wxT(AUDACITY_NAME);
|
|
wxString vendorName = wxT(AUDACITY_NAME);
|
|
#else
|
|
wxString vendorName = wxT("Audacity");
|
|
wxString appName = wxT("Audacity");
|
|
#endif
|
|
|
|
wxTheApp->SetVendorName(vendorName);
|
|
wxTheApp->SetAppName(appName);
|
|
|
|
// Unused strings that we want to be translated, even though
|
|
// we're not using them yet...
|
|
wxString future1 = _("Master Gain Control");
|
|
|
|
::wxInitAllImageHandlers();
|
|
|
|
wxFileSystem::AddHandler(new wxZipFSHandler);
|
|
|
|
InitPreferences();
|
|
|
|
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
|
|
this->AssociateFileTypes();
|
|
#endif
|
|
|
|
// TODO - read the number of files to store in history from preferences
|
|
mRecentFiles = new FileHistory(ID_RECENT_LAST - ID_RECENT_FIRST + 1, ID_RECENT_CLEAR);
|
|
mRecentFiles->Load(*gPrefs, wxT("RecentFiles"));
|
|
|
|
//
|
|
// Paths: set search path and temp dir path
|
|
//
|
|
|
|
wxString home = wxGetHomeDir();
|
|
theTheme.EnsureInitialised();
|
|
|
|
// AColor depends on theTheme.
|
|
AColor::Init();
|
|
|
|
/* Search path (for plug-ins, translations etc) is (in this order):
|
|
* The AUDACITY_PATH environment variable
|
|
* The current directory
|
|
* The user's .audacity-files directory in their home directory
|
|
* The "share" and "share/doc" directories in their install path */
|
|
#ifdef __WXGTK__
|
|
/* On Unix systems, the default temp dir is in /var/tmp. */
|
|
defaultTempDir.Printf(wxT("/var/tmp/audacity-%s"), wxGetUserId().c_str());
|
|
|
|
wxString pathVar = wxGetenv(wxT("AUDACITY_PATH"));
|
|
if (pathVar != wxT(""))
|
|
AddMultiPathsToPathList(pathVar, audacityPathList);
|
|
AddUniquePathToPathList(::wxGetCwd(), audacityPathList);
|
|
#ifdef AUDACITY_NAME
|
|
AddUniquePathToPathList(wxString::Format(wxT("%s/.%s-files"),
|
|
home.c_str(), wxT(AUDACITY_NAME)),
|
|
audacityPathList);
|
|
AddUniquePathToPathList(wxString::Format(wxT("%s/share/%s"),
|
|
wxT(INSTALL_PREFIX), wxT(AUDACITY_NAME)),
|
|
audacityPathList);
|
|
AddUniquePathToPathList(wxString::Format(wxT("%s/share/doc/%s"),
|
|
wxT(INSTALL_PREFIX), wxT(AUDACITY_NAME)),
|
|
audacityPathList);
|
|
#else //AUDACITY_NAME
|
|
AddUniquePathToPathList(wxString::Format(wxT("%s/.audacity-files"),
|
|
home.c_str()),
|
|
audacityPathList);
|
|
AddUniquePathToPathList(wxString::Format(wxT("%s/share/audacity"),
|
|
wxT(INSTALL_PREFIX)),
|
|
audacityPathList);
|
|
AddUniquePathToPathList(wxString::Format(wxT("%s/share/doc/audacity"),
|
|
wxT(INSTALL_PREFIX)),
|
|
audacityPathList);
|
|
#endif //AUDACITY_NAME
|
|
|
|
AddUniquePathToPathList(wxString::Format(wxT("%s/share/locale"),
|
|
wxT(INSTALL_PREFIX)),
|
|
audacityPathList);
|
|
|
|
AddUniquePathToPathList(wxString::Format(wxT("./locale")),
|
|
audacityPathList);
|
|
|
|
#endif //__WXGTK__
|
|
|
|
wxFileName tmpFile;
|
|
tmpFile.AssignTempFileName(wxT("nn"));
|
|
wxString tmpDirLoc = tmpFile.GetPath(wxPATH_GET_VOLUME);
|
|
::wxRemoveFile(tmpFile.GetFullPath());
|
|
|
|
// On Mac and Windows systems, use the directory which contains Audacity.
|
|
#ifdef __WXMSW__
|
|
// On Windows, the path to the Audacity program is in argv[0]
|
|
wxString progPath = wxPathOnly(argv[0]);
|
|
AddUniquePathToPathList(progPath, audacityPathList);
|
|
AddUniquePathToPathList(progPath+wxT("\\Languages"), audacityPathList);
|
|
|
|
defaultTempDir.Printf(wxT("%s\\audacity_temp"),
|
|
tmpDirLoc.c_str());
|
|
#endif //__WXWSW__
|
|
|
|
#ifdef __WXMAC__
|
|
// On Mac OS X, the path to the Audacity program is in argv[0]
|
|
wxString progPath = wxPathOnly(argv[0]);
|
|
|
|
AddUniquePathToPathList(progPath, audacityPathList);
|
|
// If Audacity is a "bundle" package, then the root directory is
|
|
// the great-great-grandparent of the directory containing the executable.
|
|
AddUniquePathToPathList(progPath+wxT("/../../../"), audacityPathList);
|
|
|
|
// These allow for searching the "bundle"
|
|
AddUniquePathToPathList(progPath+wxT("/../"), audacityPathList);
|
|
AddUniquePathToPathList(progPath+wxT("/../Resources"), audacityPathList);
|
|
|
|
defaultTempDir.Printf(wxT("%s/audacity-%s"),
|
|
tmpDirLoc.c_str(),
|
|
wxGetUserId().c_str());
|
|
#endif //__WXMAC__
|
|
|
|
// Locale
|
|
// wxWidgets 2.3 has a much nicer wxLocale API. We can make this code much
|
|
// better once we move to wx 2.3/2.4.
|
|
|
|
wxString lang = gPrefs->Read(wxT("/Locale/Language"), wxT(""));
|
|
|
|
if (lang == wxT(""))
|
|
lang = GetSystemLanguageCode();
|
|
|
|
mLocale = NULL;
|
|
InitLang( lang );
|
|
|
|
// Init DirManager, which initializes the temp directory
|
|
// If this fails, we must exit the program.
|
|
|
|
if (!InitTempDir()) {
|
|
FinishPreferences();
|
|
return false;
|
|
}
|
|
|
|
//JKC We'd like to initialise the module manager WHILE showing the splash screen.
|
|
//Can't in wx3.0.1 as MultiDialog interacts poorly with the splash screen. So we do it before.
|
|
//TODO: Find out why opening a multidialog wrecks the splash screen.
|
|
//best current guess is that it's something to do with doing a DoModal this early
|
|
//in the program.
|
|
|
|
// Initialize the CommandHandler
|
|
InitCommandHandler();
|
|
|
|
// Initialize the PluginManager
|
|
PluginManager::Get().Initialize();
|
|
|
|
// Initialize the ModuleManager, including loading found modules
|
|
ModuleManager::Get().Initialize(*mCmdHandler);
|
|
|
|
#if !wxCHECK_VERSION(3, 0, 0)
|
|
FinishInits();
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
#if wxCHECK_VERSION(3, 0, 0)
|
|
#include <wx/evtloop.h>
|
|
static bool bInitsDone = false;
|
|
void AudacityApp::OnEventLoopEnter(wxEventLoopBase * pLoop)
|
|
{
|
|
if( !pLoop->IsMain() )
|
|
return;
|
|
if (bInitsDone)
|
|
return;
|
|
bInitsDone = true;
|
|
FinishInits();
|
|
}
|
|
#endif
|
|
|
|
// JKC: I've split 'FinishInits()' from 'OnInit()', so that
|
|
// we can have a real event loop running. We could (I think)
|
|
// put everything that is in OnInit() in here.
|
|
// This change was to support wxWidgets 3.0.0 and allow us
|
|
// to show a dialog (for module loading) during initialisation.
|
|
// without it messing up the splash screen.
|
|
// Hasn't actually fixed that yet, but is addressing the point
|
|
// they make in their release notes.
|
|
void AudacityApp::FinishInits()
|
|
{
|
|
|
|
// No Splash screen on wx3 whislt we sort out the problem
|
|
// with showing a dialog AND a splash screen during inits.
|
|
#if !wxCHECK_VERSION(3, 0, 0)
|
|
// BG: Create a temporary window to set as the top window
|
|
wxImage logoimage((const char **) AudacityLogoWithName_xpm);
|
|
logoimage.Rescale(logoimage.GetWidth() / 2, logoimage.GetHeight() / 2);
|
|
wxBitmap logo(logoimage);
|
|
|
|
wxSplashScreen *temporarywindow =
|
|
new wxSplashScreen(logo,
|
|
wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_NO_TIMEOUT,
|
|
0,
|
|
NULL,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
wxSTAY_ON_TOP);
|
|
temporarywindow->SetTitle(_("Audacity is starting up..."));
|
|
SetTopWindow(temporarywindow);
|
|
#endif
|
|
|
|
//JKC: Would like to put module loading here.
|
|
|
|
// More initialization
|
|
|
|
InitDitherers();
|
|
InitAudioIO();
|
|
|
|
LoadEffects();
|
|
|
|
#ifdef __WXMAC__
|
|
|
|
// On the Mac, users don't expect a program to quit when you close the last window.
|
|
// Create a menubar that will show when all project windows are closed.
|
|
|
|
wxMenu *fileMenu = new wxMenu();
|
|
wxMenu *recentMenu = new wxMenu();
|
|
fileMenu->Append(wxID_NEW, wxString(_("&New")) + wxT("\tCtrl+N"));
|
|
fileMenu->Append(wxID_OPEN, wxString(_("&Open...")) + wxT("\tCtrl+O"));
|
|
fileMenu->AppendSubMenu(recentMenu, _("Open &Recent..."));
|
|
fileMenu->Append(wxID_ABOUT, _("&About Audacity..."));
|
|
fileMenu->Append(wxID_PREFERENCES, wxString(_("&Preferences...")) + wxT("\tCtrl+,"));
|
|
|
|
wxMenuBar *menuBar = new wxMenuBar();
|
|
menuBar->Append(fileMenu, _("&File"));
|
|
|
|
wxMenuBar::MacSetCommonMenuBar(menuBar);
|
|
|
|
mRecentFiles->UseMenu(recentMenu);
|
|
mRecentFiles->AddFilesToMenu(recentMenu);
|
|
|
|
// This invisibale frame will be the "root" of all other frames and will
|
|
// become the active frame when no projects are open.
|
|
gParentFrame = new wxFrame(NULL, -1, wxEmptyString, wxPoint(0, 0), wxSize(0, 0), 0);
|
|
|
|
#endif //__WXMAC__
|
|
|
|
SetExitOnFrameDelete(true);
|
|
|
|
|
|
AudacityProject *project = CreateNewAudacityProject();
|
|
mCmdHandler->SetProject(project);
|
|
wxWindow * pWnd = MakeHijackPanel() ;
|
|
if( pWnd )
|
|
{
|
|
project->Show( false );
|
|
pWnd->SetParent( project );
|
|
SetTopWindow(pWnd);
|
|
pWnd->Show( true );
|
|
}
|
|
|
|
|
|
#if !wxCHECK_VERSION(3, 0, 0)
|
|
temporarywindow->Show(false);
|
|
delete temporarywindow;
|
|
#endif
|
|
|
|
if( project->mShowSplashScreen )
|
|
project->OnHelpWelcome();
|
|
|
|
// JKC 10-Sep-2007: Enable monitoring from the start.
|
|
// (recommended by lprod.org).
|
|
// Monitoring stops again after any
|
|
// PLAY or RECORD completes.
|
|
// So we also call StartMonitoring when STOP is called.
|
|
project->MayStartMonitoring();
|
|
|
|
#ifdef USE_FFMPEG
|
|
FFmpegStartup();
|
|
#endif
|
|
|
|
Importer::Get().Initialize();
|
|
|
|
//
|
|
// Auto-recovery
|
|
//
|
|
bool didRecoverAnything = false;
|
|
if (!ShowAutoRecoveryDialogIfNeeded(&project, &didRecoverAnything))
|
|
{
|
|
// Important: Prevent deleting any temporary files!
|
|
DirManager::SetDontDeleteTempFiles();
|
|
QuitAudacity(true);
|
|
}
|
|
|
|
//
|
|
// Command-line parsing, but only if we didn't recover
|
|
//
|
|
|
|
#if !defined(__CYGWIN__)
|
|
|
|
// Parse command-line arguments
|
|
if (argc > 1 && !didRecoverAnything) {
|
|
for (int option = 1; option < argc; option++) {
|
|
if (!argv[option])
|
|
continue;
|
|
bool handled = false;
|
|
|
|
if (!wxString(wxT("-help")).CmpNoCase(argv[option])) {
|
|
PrintCommandLineHelp(); // print the help message out
|
|
exit(0);
|
|
}
|
|
|
|
if (option < argc - 1 &&
|
|
#if wxCHECK_VERSION(3,0,0)
|
|
!argv[option + 1].IsEmpty() &&
|
|
#else
|
|
argv[option + 1] &&
|
|
#endif
|
|
!wxString(wxT("-blocksize")).CmpNoCase(argv[option])) {
|
|
long theBlockSize;
|
|
if (wxString(argv[option + 1]).ToLong(&theBlockSize)) {
|
|
if (theBlockSize >= 256 && theBlockSize < 100000000) {
|
|
wxFprintf(stderr, _("Using block size of %ld\n"),
|
|
theBlockSize);
|
|
Sequence::SetMaxDiskBlockSize(theBlockSize);
|
|
}
|
|
}
|
|
option++;
|
|
handled = true;
|
|
}
|
|
|
|
if (!handled && !wxString(wxT("-test")).CmpNoCase(argv[option])) {
|
|
RunBenchmark(NULL);
|
|
exit(0);
|
|
}
|
|
|
|
if (!handled && !wxString(wxT("-version")).CmpNoCase(argv[option])) {
|
|
wxPrintf(wxT("Audacity v%s\n"),
|
|
AUDACITY_VERSION_STRING);
|
|
exit(0);
|
|
}
|
|
|
|
if (argv[option][0] == wxT('-') && !handled) {
|
|
wxPrintf(_("Unknown command line option: %s\n"), argv[option]);
|
|
exit(0);
|
|
}
|
|
|
|
if (!handled)
|
|
{
|
|
if (!project)
|
|
{
|
|
// Create new window for project
|
|
project = CreateNewAudacityProject();
|
|
}
|
|
// Always open files with an absolute path
|
|
wxFileName fn(argv[option]);
|
|
fn.MakeAbsolute();
|
|
project->OpenFile(fn.GetFullPath());
|
|
project = NULL; // don't reuse this project for other file
|
|
}
|
|
|
|
} // for option...
|
|
} // if (argc>1)
|
|
|
|
#else //__CYGWIN__
|
|
|
|
// Cygwin command line parser (by Dave Fancella)
|
|
if (argc > 1 && !didRecoverAnything) {
|
|
int optionstart = 1;
|
|
bool startAtOffset = false;
|
|
|
|
// Scan command line arguments looking for trouble
|
|
for (int option = 1; option < argc; option++) {
|
|
if (!argv[option])
|
|
continue;
|
|
// Check to see if argv[0] is copied across other arguments.
|
|
// This is the reason Cygwin gets its own command line parser.
|
|
if (wxString(argv[option]).Lower().Contains(wxString(wxT("audacity.exe")))) {
|
|
startAtOffset = true;
|
|
optionstart = option + 1;
|
|
}
|
|
}
|
|
|
|
for (int option = optionstart; option < argc; option++) {
|
|
if (!argv[option])
|
|
continue;
|
|
bool handled = false;
|
|
bool openThisFile = false;
|
|
wxString fileToOpen;
|
|
|
|
if (!wxString(wxT("-help")).CmpNoCase(argv[option])) {
|
|
PrintCommandLineHelp(); // print the help message out
|
|
exit(0);
|
|
}
|
|
|
|
if (option < argc - 1 &&
|
|
argv[option + 1] &&
|
|
!wxString(wxT("-blocksize")).CmpNoCase(argv[option])) {
|
|
long theBlockSize;
|
|
if (wxString(argv[option + 1]).ToLong(&theBlockSize)) {
|
|
if (theBlockSize >= 256 && theBlockSize < 100000000) {
|
|
wxFprintf(stderr, _("Using block size of %ld\n"),
|
|
theBlockSize);
|
|
Sequence::SetMaxDiskBlockSize(theBlockSize);
|
|
}
|
|
}
|
|
option++;
|
|
handled = true;
|
|
}
|
|
|
|
if (!handled && !wxString(wxT("-test")).CmpNoCase(argv[option])) {
|
|
RunBenchmark(NULL);
|
|
exit(0);
|
|
}
|
|
|
|
if (argv[option][0] == wxT('-') && !handled) {
|
|
wxPrintf(_("Unknown command line option: %s\n"), argv[option]);
|
|
exit(0);
|
|
}
|
|
|
|
if(handled)
|
|
fileToOpen.Clear();
|
|
|
|
if (!handled)
|
|
fileToOpen = fileToOpen + wxT(" ") + argv[option];
|
|
if(wxString(argv[option]).Lower().Contains(wxT(".aup")))
|
|
openThisFile = true;
|
|
if(openThisFile) {
|
|
openThisFile = false;
|
|
if (!project)
|
|
{
|
|
// Create new window for project
|
|
project = CreateNewAudacityProject();
|
|
}
|
|
project->OpenFile(fileToOpen);
|
|
project = NULL; // don't reuse this project for other file
|
|
}
|
|
|
|
} // for option...
|
|
} // if (argc>1)
|
|
|
|
#endif // __CYGWIN__ (Cygwin command-line parser)
|
|
|
|
gInited = true;
|
|
|
|
ModuleManager::Get().Dispatch(AppInitialized);
|
|
|
|
mWindowRectAlreadySaved = FALSE;
|
|
}
|
|
|
|
void AudacityApp::InitCommandHandler()
|
|
{
|
|
mCmdHandler = new CommandHandler(*this);
|
|
//SetNextHandler(mCmdHandler);
|
|
}
|
|
|
|
void AudacityApp::DeInitCommandHandler()
|
|
{
|
|
wxASSERT(NULL != mCmdHandler);
|
|
delete mCmdHandler;
|
|
mCmdHandler = NULL;
|
|
}
|
|
|
|
// AppCommandEvent callback - just pass the event on to the CommandHandler
|
|
void AudacityApp::OnReceiveCommand(AppCommandEvent &event)
|
|
{
|
|
wxASSERT(NULL != mCmdHandler);
|
|
mCmdHandler->OnReceiveCommand(event);
|
|
}
|
|
|
|
bool AudacityApp::InitTempDir()
|
|
{
|
|
// We need to find a temp directory location.
|
|
|
|
wxString tempFromPrefs = gPrefs->Read(wxT("/Directories/TempDir"), wxT(""));
|
|
wxString tempDefaultLoc = wxGetApp().defaultTempDir;
|
|
|
|
wxString temp = wxT("");
|
|
|
|
#ifdef __WXGTK__
|
|
if (tempFromPrefs.Length() > 0 && tempFromPrefs[0] != wxT('/'))
|
|
tempFromPrefs = wxT("");
|
|
#endif
|
|
|
|
// Stop wxWidgets from printing its own error messages
|
|
|
|
wxLogNull logNo;
|
|
|
|
// Try temp dir that was stored in prefs first
|
|
|
|
if (tempFromPrefs != wxT("")) {
|
|
if (wxDirExists(tempFromPrefs))
|
|
temp = tempFromPrefs;
|
|
else if (wxMkdir(tempFromPrefs, 0755))
|
|
temp = tempFromPrefs;
|
|
}
|
|
|
|
// If that didn't work, try the default location
|
|
|
|
if (temp==wxT("") && tempDefaultLoc != wxT("")) {
|
|
if (wxDirExists(tempDefaultLoc))
|
|
temp = tempDefaultLoc;
|
|
else if (wxMkdir(tempDefaultLoc, 0755))
|
|
temp = tempDefaultLoc;
|
|
}
|
|
|
|
// Check temp directory ownership on *nix systems only
|
|
#ifdef __UNIX__
|
|
struct stat tempStatBuf;
|
|
if ( lstat(temp.mb_str(), &tempStatBuf) != 0 ) {
|
|
temp.clear();
|
|
}
|
|
else {
|
|
if ( geteuid() != tempStatBuf.st_uid ) {
|
|
temp.clear();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (temp == wxT("")) {
|
|
// Failed
|
|
wxMessageBox(_("Audacity could not find a place to store temporary files.\nPlease enter an appropriate directory in the preferences dialog."));
|
|
|
|
PrefsDialog dialog(NULL);
|
|
dialog.ShowTempDirPage();
|
|
dialog.ShowModal();
|
|
|
|
wxMessageBox(_("Audacity is now going to exit. Please launch Audacity again to use the new temporary directory."));
|
|
return false;
|
|
}
|
|
|
|
// The permissions don't always seem to be set on
|
|
// some platforms. Hopefully this fixes it...
|
|
#ifdef __UNIX__
|
|
chmod(OSFILENAME(temp), 0755);
|
|
#endif
|
|
|
|
bool bSuccess = gPrefs->Write(wxT("/Directories/TempDir"), temp) && gPrefs->Flush();
|
|
DirManager::SetTempDir(temp);
|
|
|
|
// Make sure the temp dir isn't locked by another process.
|
|
if (!CreateSingleInstanceChecker(temp))
|
|
return false;
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
// Return true if there are no other instances of Audacity running,
|
|
// false otherwise.
|
|
//
|
|
// Use "dir" for creating lockfiles (on OS X and Unix).
|
|
|
|
bool AudacityApp::CreateSingleInstanceChecker(wxString dir)
|
|
{
|
|
wxLogNull dontLog;
|
|
|
|
wxString name = wxString::Format(wxT("audacity-lock-%s"), wxGetUserId().c_str());
|
|
mChecker = new wxSingleInstanceChecker();
|
|
|
|
wxString runningTwoCopiesStr = _("Running two copies of Audacity simultaneously may cause\ndata loss or cause your system to crash.\n\n");
|
|
|
|
if (!mChecker->Create(name, dir)) {
|
|
// Error initializing the wxSingleInstanceChecker. We don't know
|
|
// whether there is another instance running or not.
|
|
|
|
wxString prompt =
|
|
_("Audacity was not able to lock the temporary files directory.\nThis folder may be in use by another copy of Audacity.\n") +
|
|
runningTwoCopiesStr +
|
|
_("Do you still want to start Audacity?");
|
|
int action = wxMessageBox(prompt,
|
|
_("Error Locking Temporary Folder"),
|
|
wxYES_NO | wxICON_EXCLAMATION,
|
|
NULL);
|
|
if (action == wxNO) {
|
|
delete mChecker;
|
|
return false;
|
|
}
|
|
}
|
|
else if ( mChecker->IsAnotherRunning() ) {
|
|
#if defined(__WXMSW__)
|
|
//
|
|
// On Windows (and possibly Linux?), we attempt to make
|
|
// a DDE connection to an already active Audacity. If
|
|
// successful, we send the first command line argument
|
|
// (the audio file name) to that Audacity for processing.
|
|
wxClient client;
|
|
wxConnectionBase *conn;
|
|
|
|
// We try up to 10 times since there's a small window
|
|
// where the DDE server may not have been fully initialized
|
|
// yet.
|
|
for (int i = 0; i < 10; i++) {
|
|
conn = client.MakeConnection(wxEmptyString,
|
|
IPC_APPL,
|
|
IPC_TOPIC);
|
|
if (conn) {
|
|
if (conn->Execute(argv[1])) {
|
|
// Command was successfully queued so exit quietly
|
|
delete mChecker;
|
|
return false;
|
|
}
|
|
}
|
|
wxMilliSleep(100);
|
|
}
|
|
#endif
|
|
// There is another copy of Audacity running. Force quit.
|
|
|
|
wxString prompt =
|
|
_("The system has detected that another copy of Audacity is running.\n") +
|
|
runningTwoCopiesStr +
|
|
_("Use the New or Open commands in the currently running Audacity\nprocess to open multiple projects simultaneously.\n");
|
|
wxMessageBox(prompt, _("Audacity is already running"),
|
|
wxOK | wxICON_ERROR);
|
|
delete mChecker;
|
|
return false;
|
|
}
|
|
|
|
#if defined(__WXMSW__)
|
|
// Create the DDE server
|
|
mIPCServ = new IPCServ();
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void AudacityApp::PrintCommandLineHelp(void)
|
|
{
|
|
wxPrintf(wxT("%s\n%s\n%s\n%s\n%s\n\n%s\n"),
|
|
_("Command-line options supported:"),
|
|
/*i18n-hint: '-help' is the option and needs to stay in
|
|
* English. This displays a list of available options */
|
|
_("\t-help (this message)"),
|
|
/*i18n-hint '-version' needs to stay in English. */
|
|
_("\t-version (display Audacity version)"),
|
|
/*i18n-hint '-test' is the option and needs to stay in
|
|
* English. This runs a set of automatic tests on audacity
|
|
* itself */
|
|
_("\t-test (run self diagnostics)"),
|
|
/*i18n-hint '-blocksize' is the option and needs to stay in
|
|
* English. 'nnn' is any integer number. This controls the
|
|
* size pieces that audacity uses when writing files to the
|
|
* disk */
|
|
_("\t-blocksize nnn (set max disk block size in bytes)"),
|
|
_("In addition, specify the name of an audio file or Audacity project to open it."));
|
|
|
|
}
|
|
|
|
// static
|
|
void AudacityApp::AddUniquePathToPathList(wxString path,
|
|
wxArrayString &pathList)
|
|
{
|
|
wxFileName pathNorm = path;
|
|
pathNorm.Normalize();
|
|
path = pathNorm.GetFullPath();
|
|
|
|
for(unsigned int i=0; i<pathList.GetCount(); i++) {
|
|
if (wxFileName(path) == wxFileName(pathList[i]))
|
|
return;
|
|
}
|
|
|
|
pathList.Add(path);
|
|
}
|
|
|
|
// static
|
|
void AudacityApp::AddMultiPathsToPathList(wxString multiPathString,
|
|
wxArrayString &pathList)
|
|
{
|
|
while (multiPathString != wxT("")) {
|
|
wxString onePath = multiPathString.BeforeFirst(wxPATH_SEP[0]);
|
|
multiPathString = multiPathString.AfterFirst(wxPATH_SEP[0]);
|
|
AddUniquePathToPathList(onePath, pathList);
|
|
}
|
|
}
|
|
|
|
// static
|
|
void AudacityApp::FindFilesInPathList(const wxString & pattern,
|
|
const wxArrayString & pathList,
|
|
wxArrayString & results,
|
|
int flags)
|
|
{
|
|
wxLogNull nolog;
|
|
|
|
if (pattern == wxT("")) {
|
|
return;
|
|
}
|
|
|
|
wxFileName f;
|
|
|
|
for(size_t i = 0; i < pathList.GetCount(); i++) {
|
|
f = pathList[i] + wxFILE_SEP_PATH + pattern;
|
|
wxDir::GetAllFiles(f.GetPath(), &results, f.GetFullName(), flags);
|
|
}
|
|
}
|
|
|
|
void AudacityApp::OnEndSession(wxCloseEvent & event)
|
|
{
|
|
bool force = !event.CanVeto();
|
|
|
|
// Try to close each open window. If the user hits Cancel
|
|
// in a Save Changes dialog, don't continue.
|
|
if (!gAudacityProjects.IsEmpty()) {
|
|
while (gAudacityProjects.Count()) {
|
|
if (force) {
|
|
gAudacityProjects[0]->Close(true);
|
|
}
|
|
else if (!gAudacityProjects[0]->Close()) {
|
|
gIsQuitting = false;
|
|
event.Veto();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AudacityApp::OnKeyDown(wxKeyEvent & event)
|
|
{
|
|
// Not handled
|
|
event.Skip(true);
|
|
|
|
// Make sure this event is destined for a project window
|
|
AudacityProject *prj = GetActiveProject();
|
|
|
|
// TODO: I don't know how it can happen, but it did on 2006-07-06.
|
|
// I was switching between apps fairly quickly so maybe that has something
|
|
// to do with it.
|
|
if (!prj)
|
|
return;
|
|
|
|
if (prj != wxGetTopLevelParent(wxWindow::FindFocus()))
|
|
return;
|
|
|
|
if (prj->HandleKeyDown(event))
|
|
event.Skip(false);
|
|
}
|
|
|
|
void AudacityApp::OnChar(wxKeyEvent & event)
|
|
{
|
|
// Not handled
|
|
event.Skip(true);
|
|
|
|
// Make sure this event is destined for a project window
|
|
AudacityProject *prj = GetActiveProject();
|
|
|
|
// TODO: I don't know how it can happen, but it did on 2006-07-06.
|
|
// I was switching between apps fairly quickly so maybe that has something
|
|
// to do with it.
|
|
if (!prj)
|
|
return;
|
|
|
|
if (prj != wxGetTopLevelParent(wxWindow::FindFocus()))
|
|
return;
|
|
|
|
if (prj->HandleChar(event))
|
|
event.Skip(false);
|
|
}
|
|
|
|
void AudacityApp::OnKeyUp(wxKeyEvent & event)
|
|
{
|
|
// Not handled
|
|
event.Skip(true);
|
|
|
|
// Make sure this event is destined for a project window
|
|
AudacityProject *prj = GetActiveProject();
|
|
|
|
// TODO: I don't know how it can happen, but it did on 2006-07-06.
|
|
// I was switching between apps fairly quickly so maybe that has something
|
|
// to do with it.
|
|
if (!prj)
|
|
return;
|
|
|
|
if (prj != wxGetTopLevelParent(wxWindow::FindFocus()))
|
|
return;
|
|
|
|
if (prj->HandleKeyUp(event))
|
|
event.Skip(false);
|
|
}
|
|
|
|
void AudacityApp::AddFileToHistory(const wxString & name)
|
|
{
|
|
mRecentFiles->AddFileToHistory(name);
|
|
}
|
|
|
|
int AudacityApp::OnExit()
|
|
{
|
|
gIsQuitting = true;
|
|
while(Pending())
|
|
{
|
|
Dispatch();
|
|
}
|
|
|
|
#if defined(__WXMSW__)
|
|
delete mIPCServ;
|
|
#endif
|
|
|
|
Importer::Get().Terminate();
|
|
|
|
if(gPrefs)
|
|
{
|
|
bool bFalse = false;
|
|
//Should we change the commands.cfg location next startup?
|
|
if(gPrefs->Read(wxT("/QDeleteCmdCfgLocation"), &bFalse))
|
|
{
|
|
gPrefs->DeleteEntry(wxT("/QDeleteCmdCfgLocation"));
|
|
gPrefs->Write(wxT("/DeleteCmdCfgLocation"), true);
|
|
gPrefs->Flush();
|
|
}
|
|
}
|
|
|
|
DeInitCommandHandler();
|
|
|
|
mRecentFiles->Save(*gPrefs, wxT("RecentFiles"));
|
|
delete mRecentFiles;
|
|
|
|
FinishPreferences();
|
|
|
|
#ifdef USE_FFMPEG
|
|
DropFFmpegLibs();
|
|
#endif
|
|
|
|
UnloadEffects();
|
|
|
|
DeinitFFT();
|
|
BlockFile::Deinit();
|
|
|
|
DeinitAudioIO();
|
|
|
|
// Terminate the PluginManager (must be done before deleting the locale)
|
|
PluginManager::Get().Terminate();
|
|
|
|
if (mLocale)
|
|
delete mLocale;
|
|
delete mChecker;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// The following five methods are currently only used on Mac OS,
|
|
// where it's possible to have a menu bar but no windows open.
|
|
// It doesn't hurt any other platforms, though.
|
|
|
|
// ...That is, as long as you check to see if no windows are open
|
|
// before executing the stuff.
|
|
// To fix this, check to see how many project windows are open,
|
|
// and skip the event unless none are open (which should only happen
|
|
// on the Mac, at least currently.)
|
|
|
|
void AudacityApp::OnMenuAbout(wxCommandEvent & event)
|
|
{
|
|
// This function shadows a similar function
|
|
// in Menus.cpp, but should only be used on the Mac platform
|
|
// when no project windows are open. This check assures that
|
|
// this happens, and enable the same code to be present on
|
|
// all platforms.
|
|
if(gAudacityProjects.GetCount() == 0) {
|
|
AboutDialog dlog(NULL);
|
|
dlog.ShowModal();
|
|
}
|
|
else
|
|
event.Skip();
|
|
}
|
|
|
|
void AudacityApp::OnMenuNew(wxCommandEvent & event)
|
|
{
|
|
// This function shadows a similar function
|
|
// in Menus.cpp, but should only be used on the Mac platform
|
|
// when no project windows are open. This check assures that
|
|
// this happens, and enable the same code to be present on
|
|
// all platforms.
|
|
|
|
if(gAudacityProjects.GetCount() == 0)
|
|
CreateNewAudacityProject();
|
|
else
|
|
event.Skip();
|
|
}
|
|
|
|
|
|
void AudacityApp::OnMenuOpen(wxCommandEvent & event)
|
|
{
|
|
// This function shadows a similar function
|
|
// in Menus.cpp, but should only be used on the Mac platform
|
|
// when no project windows are open. This check assures that
|
|
// this happens, and enable the same code to be present on
|
|
// all platforms.
|
|
|
|
|
|
if(gAudacityProjects.GetCount() == 0)
|
|
AudacityProject::OpenFiles(NULL);
|
|
else
|
|
event.Skip();
|
|
|
|
|
|
}
|
|
|
|
void AudacityApp::OnMenuPreferences(wxCommandEvent & event)
|
|
{
|
|
// This function shadows a similar function
|
|
// in Menus.cpp, but should only be used on the Mac platform
|
|
// when no project windows are open. This check assures that
|
|
// this happens, and enable the same code to be present on
|
|
// all platforms.
|
|
|
|
if(gAudacityProjects.GetCount() == 0) {
|
|
PrefsDialog dialog(NULL /* parent */ );
|
|
dialog.ShowModal();
|
|
}
|
|
else
|
|
event.Skip();
|
|
|
|
}
|
|
|
|
void AudacityApp::OnMenuExit(wxCommandEvent & event)
|
|
{
|
|
// This function shadows a similar function
|
|
// in Menus.cpp, but should only be used on the Mac platform
|
|
// when no project windows are open. This check assures that
|
|
// this happens, and enable the same code to be present on
|
|
// all platforms.
|
|
|
|
// LL: Removed "if" to allow closing based on final project count.
|
|
// if(gAudacityProjects.GetCount() == 0)
|
|
QuitAudacity();
|
|
|
|
// LL: Veto quit if projects are still open. This can happen
|
|
// if the user selected Cancel in a Save dialog.
|
|
event.Skip(gAudacityProjects.GetCount() == 0);
|
|
|
|
}
|
|
|
|
//BG: On Windows, associate the aup file type with Audacity
|
|
/* We do this in the Windows installer now,
|
|
to avoid issues where user doesn't have admin privileges, but
|
|
in case that didn't work, allow the user to decide at startup.
|
|
|
|
//v Should encapsulate this & allow access from Prefs, too,
|
|
// if people want to manually change associations.
|
|
*/
|
|
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
|
|
void AudacityApp::AssociateFileTypes()
|
|
{
|
|
wxRegKey associateFileTypes;
|
|
associateFileTypes.SetName(wxT("HKCR\\.AUP"));
|
|
bool bKeyExists = associateFileTypes.Exists();
|
|
if (!bKeyExists) {
|
|
// Not at HKEY_CLASSES_ROOT. Try HKEY_CURRENT_USER.
|
|
associateFileTypes.SetName(wxT("HKCU\\Software\\Classes\\.AUP"));
|
|
bKeyExists = associateFileTypes.Exists();
|
|
}
|
|
if (!bKeyExists) {
|
|
// File types are not currently associated.
|
|
// Check pref in case user has already decided against it.
|
|
bool bWantAssociateFiles = true;
|
|
if (!gPrefs->Read(wxT("/WantAssociateFiles"), &bWantAssociateFiles) ||
|
|
bWantAssociateFiles) {
|
|
// Either there's no pref or user does want associations
|
|
// and they got stepped on, so ask.
|
|
int wantAssoc =
|
|
wxMessageBox(
|
|
_("Audacity project (.AUP) files are not currently \nassociated with Audacity. \n\nAssociate them, so they open on double-click?"),
|
|
_("Audacity Project Files"),
|
|
wxYES_NO | wxICON_QUESTION);
|
|
if (wantAssoc == wxYES) {
|
|
gPrefs->Write(wxT("/WantAssociateFiles"), true);
|
|
gPrefs->Flush();
|
|
|
|
wxString root_key;
|
|
|
|
root_key = wxT("HKCU\\Software\\Classes\\");
|
|
associateFileTypes.SetName(root_key + wxT(".AUP")); // Start again with HKEY_CLASSES_ROOT.
|
|
if (!associateFileTypes.Create(true)) {
|
|
// Not at HKEY_CLASSES_USER. Try HKEY_CURRENT_ROOT.
|
|
root_key = wxT("HKCR\\");
|
|
associateFileTypes.SetName(root_key + wxT(".AUP"));
|
|
if (!associateFileTypes.Create(true)) {
|
|
// Actually, can't create keys. Empty root_key to flag failure.
|
|
root_key.Empty();
|
|
}
|
|
}
|
|
if (root_key.IsEmpty()) {
|
|
//v Warn that we can't set keys. Ask whether to set pref for no retry?
|
|
} else {
|
|
associateFileTypes = wxT("Audacity.Project"); // Finally set value for .AUP key
|
|
|
|
associateFileTypes.SetName(root_key + wxT("Audacity.Project"));
|
|
if(!associateFileTypes.Exists()) {
|
|
associateFileTypes.Create(true);
|
|
associateFileTypes = wxT("Audacity Project File");
|
|
}
|
|
|
|
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell"));
|
|
if(!associateFileTypes.Exists()) {
|
|
associateFileTypes.Create(true);
|
|
associateFileTypes = wxT("");
|
|
}
|
|
|
|
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open"));
|
|
if(!associateFileTypes.Exists()) {
|
|
associateFileTypes.Create(true);
|
|
}
|
|
|
|
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\command"));
|
|
wxString tmpRegAudPath;
|
|
if(associateFileTypes.Exists()) {
|
|
tmpRegAudPath = wxString(associateFileTypes).Lower();
|
|
}
|
|
if (!associateFileTypes.Exists() ||
|
|
(tmpRegAudPath.Find(wxT("audacity.exe")) >= 0)) {
|
|
associateFileTypes.Create(true);
|
|
associateFileTypes = (wxString)argv[0] + (wxString)wxT(" \"%1\"");
|
|
}
|
|
|
|
#if 0
|
|
// These can be use later to support more startup messages
|
|
// like maybe "Import into existing project" or some such.
|
|
// Leaving here for an example...
|
|
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec"));
|
|
if(!associateFileTypes.Exists()) {
|
|
associateFileTypes.Create(true);
|
|
associateFileTypes = wxT("%1");
|
|
}
|
|
|
|
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Application"));
|
|
if(!associateFileTypes.Exists()) {
|
|
associateFileTypes.Create(true);
|
|
associateFileTypes = IPC_APPL;
|
|
}
|
|
|
|
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Topic"));
|
|
if(!associateFileTypes.Exists()) {
|
|
associateFileTypes.Create(true);
|
|
associateFileTypes = IPC_TOPIC;
|
|
}
|
|
#endif
|
|
}
|
|
} else {
|
|
// User said no. Set a pref so we don't keep asking.
|
|
gPrefs->Write(wxT("/WantAssociateFiles"), false);
|
|
gPrefs->Flush();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|