From b7d6af1d87d34952c044f5be8ceb4ac61625fc92 Mon Sep 17 00:00:00 2001 From: James Crook Date: Thu, 23 Apr 2015 22:26:10 +0100 Subject: [PATCH] Added "Check for updates" menu item. Also fixed line endings on AudacityApp.cpp. --- src/AudacityApp.cpp | 4506 +++++++++++++++++++++---------------------- src/Menus.cpp | 8 +- src/Menus.h | 1 + 3 files changed, 2261 insertions(+), 2254 deletions(-) diff --git a/src/AudacityApp.cpp b/src/AudacityApp.cpp index a203e3e51..58bc0f380 100644 --- a/src/AudacityApp.cpp +++ b/src/AudacityApp.cpp @@ -1,194 +1,194 @@ -/********************************************************************** - - 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://vld.codeplex.com/ -#include -#endif - -#include "Audacity.h" // This should always be included first - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#ifdef __WXGTK__ -#include -#endif - -// chmod, lstat, geteuid -#ifdef __UNIX__ -#include -#include -#include -#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/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" - -#if defined(EXPERIMENTAL_CRASH_REPORT) -#include -#include -#include -#endif - -#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_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 +/********************************************************************** + + 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://vld.codeplex.com/ +#include +#endif + +#include "Audacity.h" // This should always be included first + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#ifdef __WXGTK__ +#include +#endif + +// chmod, lstat, geteuid +#ifdef __UNIX__ +#include +#include +#include +#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/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" + +#if defined(EXPERIMENTAL_CRASH_REPORT) +#include +#include +#include +#endif + +#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_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 # if defined(__WXDEBUG__) # define D "d" @@ -202,2068 +202,2068 @@ It handles initialization and termination by subclassing wxApp. # else # define V "28" # endif - -# if defined(EXPERIMENTAL_CRASH_REPORT) -# pragma comment(lib, "wxmsw" V "u" D "_qa") -# endif + +# if defined(EXPERIMENTAL_CRASH_REPORT) +# pragma comment(lib, "wxmsw" V "u" D "_qa") +# endif # undef V # undef D - -#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 -/* 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 - -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 - -// Where drag/drop or "Open With" filenames get stored until -// the timer routine gets around to picking them up. -static wxArrayString ofqueue; - -// -// 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 OnExec(const wxString & WXUNUSED(topic), - const wxString & data) - { - // Add the filename to the queue. It will be opened by - // the OnTimer() event when it is safe to do so. - ofqueue.Add(data); - - return true; - } - -#if !wxCHECK_VERSION(3, 0, 0) - bool OnExecute(const wxString & topic, - wxChar *data, - int WXUNUSED(size), - wxIPCFormat WXUNUSED(format)) - { - return OnExec(topic, data); - } -#endif -}; - -class IPCServ : public wxServer -{ -public: - IPCServ(const wxString & appl) - : wxServer() - { - Create(appl); - }; - - ~IPCServ() - { - }; - - wxConnectionBase *OnAcceptConnection(const wxString & topic) - { - if (topic != IPC_TOPIC) { - return NULL; - } - - return new IPCConn(); - }; -}; - -#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. -// -// 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__ - -// 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 - -// IPC communication -#define ID_IPC_SERVER 6200 -#define ID_IPC_SOCKET 6201 - -// 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 - -#ifndef __WXMSW__ - EVT_SOCKET(ID_IPC_SERVER, AudacityApp::OnServerEvent) - EVT_SOCKET(ID_IPC_SOCKET, AudacityApp::OnSocketEvent) -#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)) -{ - // 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 (gInited) { - if (ofqueue.GetCount()) { - // Load each file on the queue - while (ofqueue.GetCount()) { - wxString name(ofqueue[0]); - ofqueue.RemoveAt(0); - - // Get the user's attention if no file name was specified - if (name.IsEmpty()) { - // Get the users attention - AudacityProject *project = GetActiveProject(); - if (project) { - project->Maximize(); - project->Raise(); - project->RequestUserAttention(); - } - continue; - } - - // 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")); - } - } - } - } - - // 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() -{ - // Use dynamic_cast so that we get a NULL ptr if we haven't yet - // setup our logger. - return dynamic_cast(wxLog::GetActiveTarget()); -} - -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 - -#if wxCHECK_VERSION(3,0,0) - mLocale = new wxLocale(wxT(""), lang, wxT(""), true); -#else - mLocale = new wxLocale(wxT(""), lang, wxT(""), true, true); -#endif - -#if defined(__WXMAC__) - if (existed) { - wxSetEnv(wxT("LANG"), oldval); - } - else { - wxUnsetEnv(wxT("LANG")); - } -#endif - - for(unsigned int i=0; iAddCatalogLookupPathPrefix(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(); -} - -void AudacityApp::OnFatalException() -{ -#if defined(EXPERIMENTAL_CRASH_REPORT) - GenerateCrashReport(wxDebugReport::Context_Exception); -#endif - - exit(-1); -} - -#if defined(EXPERIMENTAL_CRASH_REPORT) -void AudacityApp::GenerateCrashReport(wxDebugReport::Context ctx) -{ - wxDebugReportCompress rpt; - rpt.AddAll(ctx); - - wxFileName fn(FileNames::DataDir(), wxT("audacity.cfg")); - rpt.AddFile(fn.GetFullPath(), wxT("Audacity Configuration")); - rpt.AddFile(FileNames::PluginRegistry(), wxT("Plugin Registry")); - rpt.AddFile(FileNames::PluginSettings(), wxT("Plugin Settings")); - - if (ctx == wxDebugReport::Context_Current) - { - rpt.AddText(wxT("audiodev.txt"), gAudioIO->GetDeviceInfo(), wxT("Audio Device Info")); - } - - AudacityLogger *logger = GetLogger(); - if (logger) - { - rpt.AddText(wxT("log.txt"), logger->GetLog(), wxT("Audacity Log")); - } - - bool ok = wxDebugReportPreviewStd().Show(rpt); - -#if defined(__WXMSW__) - wxEventLoop::SetCriticalWindow(NULL); -#endif - - if (ok && rpt.Process()) - { - wxTextEntryDialog dlg(NULL, - _("Report generated to:"), - _("Audacity Support Data"), - rpt.GetCompressedFileName(), - wxOK | wxCENTER); - dlg.ShowModal(); - - wxLogMessage(wxT("Report generated to: %s"), - rpt.GetCompressedFileName().c_str()); - - rpt.Reset(); - } -} -#endif - -#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 - -AudacityApp::AudacityApp() -{ -#if defined(EXPERIMENTAL_CRASH_REPORT) -#if defined(wxUSE_ON_FATAL_EXCEPTION) && wxUSE_ON_FATAL_EXCEPTION - wxHandleFatalExceptions(); -#endif -#endif -} - -// 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; - - mChecker = NULL; - mIPCServ = 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 - -#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); - - // Use the system language for dialogs that are displayed before - // the user selected language is available from preferences. - mLocale = NULL; - InitLang(GetSystemLanguageCode()); - - 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__ - - // Reset the language now that translation paths and preferences are available - - wxString lang = gPrefs->Read(wxT("/Locale/Language"), wxT("")); - - if (lang == wxT("")) - lang = GetSystemLanguageCode(); - - InitLang( lang ); - - // Init DirManager, which initializes the temp directory - // If this fails, we must exit the program. - if (!InitTempDir()) { - FinishPreferences(); - return false; - } - -//<<<< Try to avoid dialogs before this point. -// The reason is that InitTempDir starts the single instance checker. -// If we're waiitng in a dialog before then we can very easily -// start multiple instances, defeating the single instance checker. - - - - - //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 -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() -{ - -// Until we are ready for wxWidgets 3.x, put a warning dialog up. -// Our problem is that distros may ship with 3.x builds as default. -// We are saying, don't. -// -// wx3 is Ok for experimental builds. -// -// Deliberately not translated. People can search for the english error -// text for more details. This error will only show in versions -// of Audacity that were incorrectly built. -// -// The intention was to do this, if needed, as part of the splash screen. -// However the splash screen is one of the things broken by wx3.0 -// changes in OnInit handling. We also can't put this dialog earlier. -#if wxCHECK_VERSION(3, 0, 0) - ShowErrorDialog( NULL, - wxT("Bad Version"), - wxT( -"Audacity should be built with wxWidgets 2.8.12.\n\n This version \ -of Audacity is using wxWidgets 3.0 or later.\n We're not ready for it yet.\n \ -Click the 'Help' button for known issue."), - wxT("http://bugzilla.audacityteam.org/buglist.cgi?keywords=wx3&resolution=---"), - true); -#endif - - - // Parse command line and handle options that might require - // immediate exit...no need to initialize all of the audio - // stuff to display the version string. - wxCmdLineParser *parser = ParseCommandLine(); - if (!parser) - { - delete parser; - - // Either user requested help or a parsing error occured - exit(1); - } - - if (parser->Found(wxT("v"))) - { - delete parser; - - wxFprintf(stderr, wxT("Audacity v%s\n"), AUDACITY_VERSION_STRING); - exit(0); - } - - long lval; - if (parser->Found(wxT("b"), &lval)) - { - if (lval < 256 || lval > 100000000) - { - delete parser; - - wxPrintf(_("Block size must be within 256 to 100000000\n")); - exit(1); - } - - Sequence::SetMaxDiskBlockSize(lval); - } - - wxString fileName; - if (parser->Found(wxT("d"), &fileName)) - { - AutoSaveFile asf; - if (asf.Decode(fileName)) - { - wxPrintf(_("File decoded successfully\n")); - } - else - { - wxPrintf(_("Decoding failed\n")); - } - exit(1); - } - -// 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(); - -#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(); - delete parser; - QuitAudacity(true); - return; - } - - // - // Remainder of command line parsing, but only if we didn't recover - // - if (!didRecoverAnything) - { - if (parser->Found(wxT("t"))) - { - delete parser; - - RunBenchmark(NULL); - return; - } - - for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++) - { - MRUOpen(parser->GetParam(i)); - } - } - - delete parser; - - gInited = true; - - ModuleManager::Get().Dispatch(AppInitialized); - - mWindowRectAlreadySaved = FALSE; - - mTimer.SetOwner(this, kAudacityAppTimerID); - mTimer.Start(200); -} - -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) -{ - wxString name = wxString::Format(wxT("audacity-lock-%s"), wxGetUserId().c_str()); - mChecker = new wxSingleInstanceChecker(); - -#if defined(__UNIX__) - wxString sockFile(FileNames::DataDir() + wxT("/.audacity.sock")); -#endif - - 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() ) { - // Parse the command line to ensure correct syntax, but - // ignore options and only use the filenames, if any. - wxCmdLineParser *parser = ParseCommandLine(); - if (!parser) - { - // Complaints have already been made - return false; - } - -#if defined(__WXMSW__) - // On Windows, we attempt to make a 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; - - // We try up to 50 times since there's a small window - // where the server may not have been fully initialized. - for (int i = 0; i < 50; i++) - { - wxConnectionBase *conn = client.MakeConnection(wxEmptyString, IPC_APPL, IPC_TOPIC); - if (conn) - { - bool ok = false; - if (parser->GetParamCount() > 0) - { - // Send each parameter to existing Audacity - for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++) - { - ok = conn->Execute(parser->GetParam(i)); - } - } - else - { - // Send an empty string to force existing Audacity to front - ok = conn->Execute(wxEmptyString); - } - - delete conn; - - if (ok) - { - delete parser; - return false; - } - } - - wxMilliSleep(10); - } -#else - // On Unix-like machines, we use a local (file based) socket to - // send the first command line argument to an already running - // Audacity. - wxUNIXaddress addr; - addr.Filename(sockFile); - - // Setup the socket - wxSocketClient *sock = new wxSocketClient(); - sock->SetFlags(wxSOCKET_WAITALL); - - // We try up to 50 times since there's a small window - // where the server may not have been fully initialized. - for (int i = 0; i < 50; i++) - { - // Connect to the existing Audacity - sock->Connect(addr, true); - if (sock->IsConnected()) - { - for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++) - { - // Send the filename - wxString param = parser->GetParam(i); - sock->WriteMsg((const wxChar *) param.c_str(), (param.Len() + 1) * sizeof(wxChar)); - } - - sock->Destroy(); - delete parser; - return false; - } - - wxMilliSleep(100); - } - - sock->Destroy(); -#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 parser; - delete mChecker; - return false; - } - -#if defined(__WXMSW__) - // Create the DDE IPC server - mIPCServ = new IPCServ(IPC_APPL); -#else - int mask = umask(077); - remove(OSFILENAME(sockFile)); - wxUNIXaddress addr; - addr.Filename(sockFile); - mIPCServ = new wxSocketServer(addr, wxSOCKET_NOWAIT); - umask(mask); - - if (!mIPCServ || !mIPCServ->IsOk()) - { - // TODO: Complain here - return false; - } - - mIPCServ->SetEventHandler(*this, ID_IPC_SERVER); - mIPCServ->SetNotify(wxSOCKET_CONNECTION_FLAG); - mIPCServ->Notify(true); -#endif - return true; -} - -#if defined(__UNIX__) -void AudacityApp::OnServerEvent(wxSocketEvent & evt) -{ - wxSocketBase *sock; - - // Accept all pending connection requests - do - { - sock = mIPCServ->Accept(false); - if (sock) - { - // Setup the socket - sock->SetEventHandler(*this, ID_IPC_SOCKET); - sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG); - sock->Notify(true); - } - } while (sock); -} - -void AudacityApp::OnSocketEvent(wxSocketEvent & evt) -{ - wxSocketBase *sock = evt.GetSocket(); - - if (evt.GetSocketEvent() == wxSOCKET_LOST) - { - sock->Destroy(); - return; - } - - // Read the length of the filename and bail if we have a short read - wxChar name[PATH_MAX]; - sock->ReadMsg(&name, sizeof(name)); - if (!sock->Error()) - { - // Add the filename to the queue. It will be opened by - // the OnTimer() event when it is safe to do so. - ofqueue.Add(name); - } -} - -#endif - -wxCmdLineParser *AudacityApp::ParseCommandLine() -{ - wxCmdLineParser *parser = new wxCmdLineParser(argc, argv); - if (!parser) - { - return NULL; - } - - /*i18n-hint: This controls the number of bytes that Audacity will - * use when writing files to the disk */ - parser->AddOption(wxT("b"), wxT("blocksize"), _("set max disk block size in bytes"), - wxCMD_LINE_VAL_NUMBER); - - /*i18n-hint: This decodes an autosave file */ - parser->AddOption(wxT("d"), wxT("decode"), _("decode an autosave file"), - wxCMD_LINE_VAL_STRING); - - /*i18n-hint: This displays a list of available options */ - parser->AddSwitch(wxT("h"), wxT("help"), _("this help message"), - wxCMD_LINE_OPTION_HELP); - - /*i18n-hint: This runs a set of automatic tests on Audacity itself */ - parser->AddSwitch(wxT("t"), wxT("test"), _("run self diagnostics")); - - /*i18n-hint: This displays the Audacity version */ - parser->AddSwitch(wxT("v"), wxT("version"), _("display Audacity version")); - - /*i18n-hint: This is a list of one or more files that Audacity - * should open upon startup */ - parser->AddParam(_("audio or project file name"), - wxCMD_LINE_VAL_STRING, - wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL); - - // Run the parser - if (parser->Parse() == 0) - { - return parser; - } - - delete parser; - - return NULL; -} - -// static -void AudacityApp::AddUniquePathToPathList(wxString path, - wxArrayString &pathList) -{ - wxFileName pathNorm = path; - pathNorm.Normalize(); - path = pathNorm.GetFullPath(); - - for(unsigned int i=0; iClose(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->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->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(); - } - - 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 - - DeinitFFT(); - BlockFile::Deinit(); - - DeinitAudioIO(); - - // Terminate the PluginManager (must be done before deleting the locale) - PluginManager::Get().Terminate(); - - // Done with plugins and modules - PluginManager::Destroy(); - ModuleManager::Destroy(); - - if (mLocale) - delete mLocale; - - if (mIPCServ) - { -#if defined(__UNIX__) - wxUNIXaddress addr; - if (mIPCServ->GetLocal(addr)) - { - remove(OSFILENAME(addr.Filename())); - } -#endif - delete mIPCServ; - } - - if (mChecker) - 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 - + +#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 +/* 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 + +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 + +// Where drag/drop or "Open With" filenames get stored until +// the timer routine gets around to picking them up. +static wxArrayString ofqueue; + +// +// 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 OnExec(const wxString & WXUNUSED(topic), + const wxString & data) + { + // Add the filename to the queue. It will be opened by + // the OnTimer() event when it is safe to do so. + ofqueue.Add(data); + + return true; + } + +#if !wxCHECK_VERSION(3, 0, 0) + bool OnExecute(const wxString & topic, + wxChar *data, + int WXUNUSED(size), + wxIPCFormat WXUNUSED(format)) + { + return OnExec(topic, data); + } +#endif +}; + +class IPCServ : public wxServer +{ +public: + IPCServ(const wxString & appl) + : wxServer() + { + Create(appl); + }; + + ~IPCServ() + { + }; + + wxConnectionBase *OnAcceptConnection(const wxString & topic) + { + if (topic != IPC_TOPIC) { + return NULL; + } + + return new IPCConn(); + }; +}; + +#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. +// +// 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__ + +// 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 + +// IPC communication +#define ID_IPC_SERVER 6200 +#define ID_IPC_SOCKET 6201 + +// 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 + +#ifndef __WXMSW__ + EVT_SOCKET(ID_IPC_SERVER, AudacityApp::OnServerEvent) + EVT_SOCKET(ID_IPC_SOCKET, AudacityApp::OnSocketEvent) +#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)) +{ + // 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 (gInited) { + if (ofqueue.GetCount()) { + // Load each file on the queue + while (ofqueue.GetCount()) { + wxString name(ofqueue[0]); + ofqueue.RemoveAt(0); + + // Get the user's attention if no file name was specified + if (name.IsEmpty()) { + // Get the users attention + AudacityProject *project = GetActiveProject(); + if (project) { + project->Maximize(); + project->Raise(); + project->RequestUserAttention(); + } + continue; + } + + // 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")); + } + } + } + } + + // 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() +{ + // Use dynamic_cast so that we get a NULL ptr if we haven't yet + // setup our logger. + return dynamic_cast(wxLog::GetActiveTarget()); +} + +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 + +#if wxCHECK_VERSION(3,0,0) + mLocale = new wxLocale(wxT(""), lang, wxT(""), true); +#else + mLocale = new wxLocale(wxT(""), lang, wxT(""), true, true); +#endif + +#if defined(__WXMAC__) + if (existed) { + wxSetEnv(wxT("LANG"), oldval); + } + else { + wxUnsetEnv(wxT("LANG")); + } +#endif + + for(unsigned int i=0; iAddCatalogLookupPathPrefix(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(); +} + +void AudacityApp::OnFatalException() +{ +#if defined(EXPERIMENTAL_CRASH_REPORT) + GenerateCrashReport(wxDebugReport::Context_Exception); +#endif + + exit(-1); +} + +#if defined(EXPERIMENTAL_CRASH_REPORT) +void AudacityApp::GenerateCrashReport(wxDebugReport::Context ctx) +{ + wxDebugReportCompress rpt; + rpt.AddAll(ctx); + + wxFileName fn(FileNames::DataDir(), wxT("audacity.cfg")); + rpt.AddFile(fn.GetFullPath(), wxT("Audacity Configuration")); + rpt.AddFile(FileNames::PluginRegistry(), wxT("Plugin Registry")); + rpt.AddFile(FileNames::PluginSettings(), wxT("Plugin Settings")); + + if (ctx == wxDebugReport::Context_Current) + { + rpt.AddText(wxT("audiodev.txt"), gAudioIO->GetDeviceInfo(), wxT("Audio Device Info")); + } + + AudacityLogger *logger = GetLogger(); + if (logger) + { + rpt.AddText(wxT("log.txt"), logger->GetLog(), wxT("Audacity Log")); + } + + bool ok = wxDebugReportPreviewStd().Show(rpt); + +#if defined(__WXMSW__) + wxEventLoop::SetCriticalWindow(NULL); +#endif + + if (ok && rpt.Process()) + { + wxTextEntryDialog dlg(NULL, + _("Report generated to:"), + _("Audacity Support Data"), + rpt.GetCompressedFileName(), + wxOK | wxCENTER); + dlg.ShowModal(); + + wxLogMessage(wxT("Report generated to: %s"), + rpt.GetCompressedFileName().c_str()); + + rpt.Reset(); + } +} +#endif + +#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 + +AudacityApp::AudacityApp() +{ +#if defined(EXPERIMENTAL_CRASH_REPORT) +#if defined(wxUSE_ON_FATAL_EXCEPTION) && wxUSE_ON_FATAL_EXCEPTION + wxHandleFatalExceptions(); +#endif +#endif +} + +// 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; + + mChecker = NULL; + mIPCServ = 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 + +#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); + + // Use the system language for dialogs that are displayed before + // the user selected language is available from preferences. + mLocale = NULL; + InitLang(GetSystemLanguageCode()); + + 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__ + + // Reset the language now that translation paths and preferences are available + + wxString lang = gPrefs->Read(wxT("/Locale/Language"), wxT("")); + + if (lang == wxT("")) + lang = GetSystemLanguageCode(); + + InitLang( lang ); + + // Init DirManager, which initializes the temp directory + // If this fails, we must exit the program. + if (!InitTempDir()) { + FinishPreferences(); + return false; + } + +//<<<< Try to avoid dialogs before this point. +// The reason is that InitTempDir starts the single instance checker. +// If we're waiitng in a dialog before then we can very easily +// start multiple instances, defeating the single instance checker. + + + + + //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 +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() +{ + +// Until we are ready for wxWidgets 3.x, put a warning dialog up. +// Our problem is that distros may ship with 3.x builds as default. +// We are saying, don't. +// +// wx3 is Ok for experimental builds. +// +// Deliberately not translated. People can search for the english error +// text for more details. This error will only show in versions +// of Audacity that were incorrectly built. +// +// The intention was to do this, if needed, as part of the splash screen. +// However the splash screen is one of the things broken by wx3.0 +// changes in OnInit handling. We also can't put this dialog earlier. +#if wxCHECK_VERSION(3, 0, 0) + ShowErrorDialog( NULL, + wxT("Bad Version"), + wxT( +"Audacity should be built with wxWidgets 2.8.12.\n\n This version \ +of Audacity is using wxWidgets 3.0 or later.\n We're not ready for it yet.\n \ +Click the 'Help' button for known issue."), + wxT("http://bugzilla.audacityteam.org/buglist.cgi?keywords=wx3&resolution=---"), + true); +#endif + + + // Parse command line and handle options that might require + // immediate exit...no need to initialize all of the audio + // stuff to display the version string. + wxCmdLineParser *parser = ParseCommandLine(); + if (!parser) + { + delete parser; + + // Either user requested help or a parsing error occured + exit(1); + } + + if (parser->Found(wxT("v"))) + { + delete parser; + + wxFprintf(stderr, wxT("Audacity v%s\n"), AUDACITY_VERSION_STRING); + exit(0); + } + + long lval; + if (parser->Found(wxT("b"), &lval)) + { + if (lval < 256 || lval > 100000000) + { + delete parser; + + wxPrintf(_("Block size must be within 256 to 100000000\n")); + exit(1); + } + + Sequence::SetMaxDiskBlockSize(lval); + } + + wxString fileName; + if (parser->Found(wxT("d"), &fileName)) + { + AutoSaveFile asf; + if (asf.Decode(fileName)) + { + wxPrintf(_("File decoded successfully\n")); + } + else + { + wxPrintf(_("Decoding failed\n")); + } + exit(1); + } + +// 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(); + +#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(); + delete parser; + QuitAudacity(true); + return; + } + + // + // Remainder of command line parsing, but only if we didn't recover + // + if (!didRecoverAnything) + { + if (parser->Found(wxT("t"))) + { + delete parser; + + RunBenchmark(NULL); + return; + } + + for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++) + { + MRUOpen(parser->GetParam(i)); + } + } + + delete parser; + + gInited = true; + + ModuleManager::Get().Dispatch(AppInitialized); + + mWindowRectAlreadySaved = FALSE; + + mTimer.SetOwner(this, kAudacityAppTimerID); + mTimer.Start(200); +} + +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) +{ + wxString name = wxString::Format(wxT("audacity-lock-%s"), wxGetUserId().c_str()); + mChecker = new wxSingleInstanceChecker(); + +#if defined(__UNIX__) + wxString sockFile(FileNames::DataDir() + wxT("/.audacity.sock")); +#endif + + 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() ) { + // Parse the command line to ensure correct syntax, but + // ignore options and only use the filenames, if any. + wxCmdLineParser *parser = ParseCommandLine(); + if (!parser) + { + // Complaints have already been made + return false; + } + +#if defined(__WXMSW__) + // On Windows, we attempt to make a 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; + + // We try up to 50 times since there's a small window + // where the server may not have been fully initialized. + for (int i = 0; i < 50; i++) + { + wxConnectionBase *conn = client.MakeConnection(wxEmptyString, IPC_APPL, IPC_TOPIC); + if (conn) + { + bool ok = false; + if (parser->GetParamCount() > 0) + { + // Send each parameter to existing Audacity + for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++) + { + ok = conn->Execute(parser->GetParam(i)); + } + } + else + { + // Send an empty string to force existing Audacity to front + ok = conn->Execute(wxEmptyString); + } + + delete conn; + + if (ok) + { + delete parser; + return false; + } + } + + wxMilliSleep(10); + } +#else + // On Unix-like machines, we use a local (file based) socket to + // send the first command line argument to an already running + // Audacity. + wxUNIXaddress addr; + addr.Filename(sockFile); + + // Setup the socket + wxSocketClient *sock = new wxSocketClient(); + sock->SetFlags(wxSOCKET_WAITALL); + + // We try up to 50 times since there's a small window + // where the server may not have been fully initialized. + for (int i = 0; i < 50; i++) + { + // Connect to the existing Audacity + sock->Connect(addr, true); + if (sock->IsConnected()) + { + for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++) + { + // Send the filename + wxString param = parser->GetParam(i); + sock->WriteMsg((const wxChar *) param.c_str(), (param.Len() + 1) * sizeof(wxChar)); + } + + sock->Destroy(); + delete parser; + return false; + } + + wxMilliSleep(100); + } + + sock->Destroy(); +#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 parser; + delete mChecker; + return false; + } + +#if defined(__WXMSW__) + // Create the DDE IPC server + mIPCServ = new IPCServ(IPC_APPL); +#else + int mask = umask(077); + remove(OSFILENAME(sockFile)); + wxUNIXaddress addr; + addr.Filename(sockFile); + mIPCServ = new wxSocketServer(addr, wxSOCKET_NOWAIT); + umask(mask); + + if (!mIPCServ || !mIPCServ->IsOk()) + { + // TODO: Complain here + return false; + } + + mIPCServ->SetEventHandler(*this, ID_IPC_SERVER); + mIPCServ->SetNotify(wxSOCKET_CONNECTION_FLAG); + mIPCServ->Notify(true); +#endif + return true; +} + +#if defined(__UNIX__) +void AudacityApp::OnServerEvent(wxSocketEvent & evt) +{ + wxSocketBase *sock; + + // Accept all pending connection requests + do + { + sock = mIPCServ->Accept(false); + if (sock) + { + // Setup the socket + sock->SetEventHandler(*this, ID_IPC_SOCKET); + sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG); + sock->Notify(true); + } + } while (sock); +} + +void AudacityApp::OnSocketEvent(wxSocketEvent & evt) +{ + wxSocketBase *sock = evt.GetSocket(); + + if (evt.GetSocketEvent() == wxSOCKET_LOST) + { + sock->Destroy(); + return; + } + + // Read the length of the filename and bail if we have a short read + wxChar name[PATH_MAX]; + sock->ReadMsg(&name, sizeof(name)); + if (!sock->Error()) + { + // Add the filename to the queue. It will be opened by + // the OnTimer() event when it is safe to do so. + ofqueue.Add(name); + } +} + +#endif + +wxCmdLineParser *AudacityApp::ParseCommandLine() +{ + wxCmdLineParser *parser = new wxCmdLineParser(argc, argv); + if (!parser) + { + return NULL; + } + + /*i18n-hint: This controls the number of bytes that Audacity will + * use when writing files to the disk */ + parser->AddOption(wxT("b"), wxT("blocksize"), _("set max disk block size in bytes"), + wxCMD_LINE_VAL_NUMBER); + + /*i18n-hint: This decodes an autosave file */ + parser->AddOption(wxT("d"), wxT("decode"), _("decode an autosave file"), + wxCMD_LINE_VAL_STRING); + + /*i18n-hint: This displays a list of available options */ + parser->AddSwitch(wxT("h"), wxT("help"), _("this help message"), + wxCMD_LINE_OPTION_HELP); + + /*i18n-hint: This runs a set of automatic tests on Audacity itself */ + parser->AddSwitch(wxT("t"), wxT("test"), _("run self diagnostics")); + + /*i18n-hint: This displays the Audacity version */ + parser->AddSwitch(wxT("v"), wxT("version"), _("display Audacity version")); + + /*i18n-hint: This is a list of one or more files that Audacity + * should open upon startup */ + parser->AddParam(_("audio or project file name"), + wxCMD_LINE_VAL_STRING, + wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL); + + // Run the parser + if (parser->Parse() == 0) + { + return parser; + } + + delete parser; + + return NULL; +} + +// static +void AudacityApp::AddUniquePathToPathList(wxString path, + wxArrayString &pathList) +{ + wxFileName pathNorm = path; + pathNorm.Normalize(); + path = pathNorm.GetFullPath(); + + for(unsigned int i=0; iClose(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->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->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(); + } + + 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 + + DeinitFFT(); + BlockFile::Deinit(); + + DeinitAudioIO(); + + // Terminate the PluginManager (must be done before deleting the locale) + PluginManager::Get().Terminate(); + + // Done with plugins and modules + PluginManager::Destroy(); + ModuleManager::Destroy(); + + if (mLocale) + delete mLocale; + + if (mIPCServ) + { +#if defined(__UNIX__) + wxUNIXaddress addr; + if (mIPCServ->GetLocal(addr)) + { + remove(OSFILENAME(addr.Filename())); + } +#endif + delete mIPCServ; + } + + if (mChecker) + 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 + diff --git a/src/Menus.cpp b/src/Menus.cpp index 8916c9c2a..e1ac5496c 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -100,6 +100,7 @@ simplifies construction of menu items. #include "toolbars/DeviceToolBar.h" #include "toolbars/MixerToolBar.h" #include "toolbars/TranscriptionToolBar.h" +#include "widgets/LinkingHtmlWindow.h" #include "Experimental.h" #include "PlatformCompatibility.h" @@ -1059,7 +1060,7 @@ void AudacityProject::CreateMenusAndCommands() #endif c->AddSeparator(); - + c->AddItem(wxT("Updates"), _("&Check for Updates..."), FN(OnCheckForUpdates)); c->AddItem(wxT("DeviceInfo"), _("Au&dio Device Info..."), FN(OnAudioDeviceInfo), AudioIONotBusyFlag, AudioIONotBusyFlag); @@ -6174,6 +6175,11 @@ void AudacityProject::OnManual() wxT("Main_Page")); } +void AudacityProject::OnCheckForUpdates() +{ + ::OpenInDefaultBrowser( wxString( wxT("http://audacityteam.org/download/")) ); +} + void AudacityProject::OnShowLog() { AudacityLogger *logger = wxGetApp().GetLogger(); diff --git a/src/Menus.h b/src/Menus.h index fef154b35..739ea8ead 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -369,6 +369,7 @@ void OnStereoToMono(int index); void OnAbout(); void OnQuickHelp(); void OnManual(); +void OnCheckForUpdates(); void OnShowLog(); void OnHelpWelcome(); void OnBenchmark();