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