2015-07-15 04:33:53 +00:00
/**********************************************************************
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
2015-08-17 16:08:58 +00:00
# include "AudacityApp.h"
2015-08-17 15:49:13 +00:00
# include "TranslatableStringArray.h"
2015-07-15 04:33:53 +00:00
# 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/snglinst.h>
# include <wx/splash.h>
# include <wx/stdpaths.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/file.h>
# include <sys/stat.h>
# endif
2016-11-08 15:10:50 +00:00
# include "AudacityException.h"
2015-07-15 04:33:53 +00:00
# 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"
Automation: AudacityCommand
This is a squash of 50 commits.
This merges the capabilities of BatchCommands and Effects using a new
AudacityCommand class. AudacityCommand provides one function to specify the
parameters, and then we leverage that one function in automation, whether by chains,
mod-script-pipe or (future) Nyquist.
- Now have AudacityCommand which is using the same mechanism as Effect
- Has configurable parameters
- Has data-entry GUI (built using shuttle GUI)
- Registers with PluginManager.
- Menu commands now provided in chains, and to python batch.
- Tested with Zoom Toggle.
- ShuttleParams now can set, get, set defaults, validate and specify
the parameters.
- Bugfix: Don't overwrite values with defaults first time out.
- Add DefineParams function for all built-in effects.
- Extend CommandContext to carry output channels for results.
We abuse EffectsManager. It handles both Effects and
AudacityCommands now. In time an Effect should become a special case of
AudacityCommand and we'll split and rename the EffectManager class.
- Don't use 'default' as a parameter name.
- Massive renaming for CommandDefinitionInterface
- EffectIdentInterface becomes EffectDefinitionInterface
- EffectAutomationParameters becomes CommandAutomationParameters
- PluginType is now a bit field.
This way we can search for related types at the same time.
- Most old batch commands made into AudacityCommands.
The ones that weren't are for a reason. They are used by mod-script-pipe
to carry commands and responses across from a non-GUI thread to the GUI
thread.
- Major tidy up of ScreenshotCommand
- Reworking of SelectCommand
- GetPreferenceCommand and SetPreferenceCommand
- GetTrackInfo and SetTrackInfo
- GetInfoCommand
- Help, Open, Save, Import and Export commands.
- Removed obsolete commands ExecMenu, GetProjectInfo and SetProjectInfo
which are now better handled by other commands.
- JSONify "GetInfo: Commands" output, i.e. commas in the right places.
- General work on better Doxygen.
- Lyrics -> LyricsPanel
- Meter -> MeterPanel
- Updated Linux makefile.
- Scripting commands added into Extra menu.
- Distinct names for previously duplicated find-clipping parameters.
- Fixed longstanding error with erroneous status field number which
previously caused an ASSERT in debug.
- Sensible formatting of numbers in Chains, 0.1 not 0.1000000000137
2018-01-14 18:51:41 +00:00
# include "commands/CommandContext.h"
2015-07-15 04:33:53 +00:00
# include "effects/Contrast.h"
# include "widgets/ASlider.h"
# include "FFmpeg.h"
# include "Internat.h"
# include "LangChoice.h"
# include "Languages.h"
2018-10-16 20:45:26 +00:00
# include "Menus.h"
2015-07-15 04:33:53 +00:00
# include "PluginManager.h"
# include "Prefs.h"
# include "Project.h"
# include "Screenshot.h"
# include "Sequence.h"
# include "WaveTrack.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"
2015-07-28 20:06:25 +00:00
# include "prefs/DirectoriesPrefs.h"
2016-04-26 12:36:14 +00:00
# include "tracks/ui/Scrubbing.h"
2015-07-15 04:33:53 +00:00
//temporarilly commented out till it is added to all projects
//#include "Profiler.h"
# include "ModuleManager.h"
# include "import/Import.h"
2015-08-31 19:50:50 +00:00
# include "Experimental.h"
2015-07-15 04:33:53 +00:00
# if defined(EXPERIMENTAL_CRASH_REPORT)
# include <wx/debugrpt.h>
# include <wx/evtloop.h>
# include <wx/textdlg.h>
# 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__)
2015-07-28 20:23:30 +00:00
//#if wxCHECK_VERSION(3, 0, 2) && !wxCHECK_VERSION(3, 1, 0)
# include <wx/init.h>
//#endif
2015-07-15 04:33:53 +00:00
// These lines ensure that Audacity gets WindowsXP themes.
// Without them we get the old-style Windows98/2000 look under XP.
# if !defined(__WXWINCE__)
2016-01-24 08:36:47 +00:00
# pragma comment(linker,"\" / manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
2015-07-15 04:33:53 +00:00
# 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
2016-05-12 15:05:44 +00:00
# if defined(_DEBUG)
2015-07-15 04:33:53 +00:00
# define D "d"
# else
# define D ""
# endif
# if wxCHECK_VERSION(3, 1, 0)
# define V "31"
# elif wxCHECK_VERSION(3, 0, 0)
# define V "30"
# else
# define V "28"
# endif
# if defined(EXPERIMENTAL_CRASH_REPORT)
# pragma comment(lib, "wxmsw" V "u" D "_qa")
# endif
2015-07-28 20:23:30 +00:00
# pragma comment(lib, "wxbase" V "u" D)
2015-07-30 07:02:59 +00:00
# pragma comment(lib, "wxbase" V "u" D "_net")
# pragma comment(lib, "wxmsw" V "u" D "_adv")
# pragma comment(lib, "wxmsw" V "u" D "_core")
# pragma comment(lib, "wxmsw" V "u" D "_html")
2015-07-28 20:23:30 +00:00
# pragma comment(lib, "wxpng" D)
# pragma comment(lib, "wxzlib" D)
# pragma comment(lib, "wxjpeg" D)
# pragma comment(lib, "wxtiff" D)
2015-07-15 04:33:53 +00:00
# undef V
# undef D
# endif //(__WXMSW__)
2017-04-02 22:07:13 +00:00
// DA: Logo for Splash Screen
# ifdef EXPERIMENTAL_DA
# include "../images/DarkAudacityLogoWithName.xpm"
# else
2015-07-15 04:33:53 +00:00
# include "../images/AudacityLogoWithName.xpm"
2017-04-02 22:07:13 +00:00
# endif
2015-07-15 04:33:53 +00:00
////////////////////////////////////////////////////////////
/// Custom events
////////////////////////////////////////////////////////////
2018-02-17 03:25:00 +00:00
wxDEFINE_EVENT ( EVT_CLIPBOARD_CHANGE , wxCommandEvent ) ;
2015-07-15 04:33:53 +00:00
DEFINE_EVENT_TYPE ( EVT_OPEN_AUDIO_FILE ) ;
2018-02-12 23:09:20 +00:00
wxDEFINE_EVENT ( EVT_LANGUAGE_CHANGE , wxCommandEvent ) ;
2015-07-15 04:33:53 +00:00
2016-02-26 19:41:17 +00:00
#if 0
2015-07-15 04:33:53 +00:00
# ifdef __WXGTK__
static void wxOnAssert ( const wxChar * fileName , int lineNumber , const wxChar * msg )
{
if ( msg )
2017-10-09 04:37:10 +00:00
wxPrintf ( " ASSERTION FAILED: %s \n %s: %d \n " , ( const char * ) wxString ( msg ) . mb_str ( ) , ( const char * ) wxString ( fileName ) . mb_str ( ) , lineNumber ) ;
2015-07-15 04:33:53 +00:00
else
2017-10-09 04:37:10 +00:00
wxPrintf ( " ASSERTION FAILED! \n %s: %d \n " , ( const char * ) wxString ( fileName ) . mb_str ( ) , lineNumber ) ;
2015-07-15 04:33:53 +00:00
// Force core dump
int * i = 0 ;
if ( * i )
exit ( 1 ) ;
exit ( 0 ) ;
}
# endif
2016-02-26 19:41:17 +00:00
# endif
2015-07-15 04:33:53 +00:00
static bool gInited = false ;
bool gIsQuitting = false ;
void QuitAudacity ( bool bForce )
{
if ( gIsQuitting )
return ;
gIsQuitting = true ;
2015-07-28 20:23:30 +00:00
wxTheApp - > SetExitOnFrameDelete ( true ) ;
2015-07-15 04:33:53 +00:00
// 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?
2019-02-12 00:10:48 +00:00
//- if (!gAudacityProjects.empty())
2015-07-15 04:33:53 +00:00
/*start+*/
2016-08-14 15:41:59 +00:00
if ( gAudacityProjects . empty ( ) )
2015-07-15 04:33:53 +00:00
{
# ifdef __WXMAC__
AudacityProject : : DeleteClipboard ( ) ;
# endif
}
else
/*end+*/
{
SaveWindowSize ( ) ;
2016-08-14 15:41:59 +00:00
while ( gAudacityProjects . size ( ) )
2015-07-15 04:33:53 +00:00
{
2016-08-14 15:41:59 +00:00
// Closing the project has global side-effect
// of deletion from gAudacityProjects
2015-07-15 04:33:53 +00:00
if ( bForce )
{
gAudacityProjects [ 0 ] - > Close ( true ) ;
}
else
{
if ( ! gAudacityProjects [ 0 ] - > Close ( ) )
{
gIsQuitting = false ;
return ;
}
}
}
}
ModuleManager : : Get ( ) . Dispatch ( AppQuiting ) ;
# 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
2016-04-08 08:32:11 +00:00
//DELETE Profiler::Instance();
2015-07-15 04:33:53 +00:00
//remove our logger
2016-02-18 17:43:02 +00:00
std : : unique_ptr < wxLog > { wxLog : : SetActiveTarget ( NULL ) } ; // DELETE
2015-07-15 04:33:53 +00:00
if ( bForce )
{
wxExit ( ) ;
}
}
void QuitAudacity ( )
{
QuitAudacity ( false ) ;
}
void SaveWindowSize ( )
{
if ( wxGetApp ( ) . GetWindowRectAlreadySaved ( ) )
{
return ;
}
bool validWindowForSaveWindowSize = FALSE ;
AudacityProject * validProject = NULL ;
bool foundIconizedProject = FALSE ;
2016-08-14 15:41:59 +00:00
size_t numProjects = gAudacityProjects . size ( ) ;
2015-07-15 04:33:53 +00:00
for ( size_t i = 0 ; i < numProjects ; i + + )
{
if ( ! gAudacityProjects [ i ] - > IsIconized ( ) ) {
validWindowForSaveWindowSize = TRUE ;
2016-08-14 15:41:59 +00:00
validProject = gAudacityProjects [ i ] . get ( ) ;
2015-07-15 04:33:53 +00:00
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 ) {
2016-08-14 15:41:59 +00:00
validProject = gAudacityProjects [ 0 ] . get ( ) ;
2015-07-15 04:33:53 +00:00
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 ;
2016-09-11 19:30:06 +00:00
static void interact_cb ( GnomeClient * /* client */ ,
2015-07-15 04:33:53 +00:00
gint key ,
2016-09-11 19:30:06 +00:00
GnomeDialogType /* type */ ,
gpointer /* data */ )
2015-07-15 04:33:53 +00:00
{
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 ,
2016-09-11 19:30:06 +00:00
gint /* phase */ ,
GnomeSaveStyle /* style */ ,
2015-07-15 04:33:53 +00:00
gboolean shutdown ,
GnomeInteractStyle interact ,
2016-09-11 19:30:06 +00:00
gboolean /* fast */ ,
gpointer /* user_data */ )
2015-07-15 04:33:53 +00:00
{
if ( ! shutdown | | interact ! = GNOME_INTERACT_ANY ) {
return TRUE ;
}
2016-08-14 15:41:59 +00:00
if ( gAudacityProjects . empty ( ) ) {
2015-07-15 04:33:53 +00:00
return TRUE ;
}
gnome_client_request_interaction ( client ,
GNOME_DIALOG_NORMAL ,
interact_cb ,
NULL ) ;
return TRUE ;
}
class GnomeShutdown
{
public :
GnomeShutdown ( )
{
2017-02-25 23:13:05 +00:00
mArgv [ 0 ] . reset ( strdup ( " Audacity " ) ) ;
2015-07-15 04:33:53 +00:00
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 ;
}
2017-02-25 23:13:05 +00:00
gnome_program_init ( mArgv [ 0 ] . get ( ) ,
2015-07-15 04:33:53 +00:00
" 1.0 " ,
libgnomeui_module_info_get ( ) ,
1 ,
2017-02-25 23:13:05 +00:00
reinterpret_cast < char * * > ( mArgv ) ,
2015-07-15 04:33:53 +00:00
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...
}
private :
2017-02-25 23:13:05 +00:00
MallocString < > mArgv [ 1 ] ;
2015-07-15 04:33:53 +00:00
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")
2016-02-24 06:06:39 +00:00
class IPCConn final : public wxConnection
2015-07-15 04:33:53 +00:00
{
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.
2019-02-12 00:10:48 +00:00
ofqueue . push_back ( data ) ;
2018-11-04 17:03:27 +00:00
2015-07-15 04:33:53 +00:00
return true ;
}
} ;
2016-02-24 06:06:39 +00:00
class IPCServ final : public wxServer
2015-07-15 04:33:53 +00:00
{
public :
IPCServ ( const wxString & appl )
: wxServer ( )
{
Create ( appl ) ;
} ;
~ IPCServ ( )
{
} ;
2016-03-31 17:28:40 +00:00
wxConnectionBase * OnAcceptConnection ( const wxString & topic ) override
2015-07-15 04:33:53 +00:00
{
if ( topic ! = IPC_TOPIC ) {
return NULL ;
}
2016-04-10 03:02:25 +00:00
// Trust wxWidgets framework to DELETE it
2016-03-31 17:28:40 +00:00
return safenew IPCConn ( ) ;
2015-07-15 04:33:53 +00:00
} ;
} ;
2015-07-30 07:02:59 +00:00
# if defined(__WXMAC__)
2017-07-27 00:15:05 +00:00
2015-07-15 04:33:53 +00:00
IMPLEMENT_APP_NO_MAIN ( AudacityApp )
IMPLEMENT_WX_THEME_SUPPORT
2015-07-28 20:23:30 +00:00
2015-07-15 04:33:53 +00:00
int main ( int argc , char * argv [ ] )
{
2015-07-28 20:23:30 +00:00
wxDISABLE_DEBUG_SUPPORT ( ) ;
2015-07-15 04:33:53 +00:00
return wxEntry ( argc , argv ) ;
}
2015-07-28 20:23:30 +00:00
2015-07-30 07:02:59 +00:00
# elif defined(__WXMSW__) && !wxCHECK_VERSION(3, 1, 0)
// Disable telling Windows that we support HiDPI displays. It is forced on
// in wxWidget versions between 3.0.0 and 3.1.0.
IMPLEMENT_APP_NO_MAIN ( AudacityApp )
IMPLEMENT_WX_THEME_SUPPORT
2015-07-28 20:23:30 +00:00
extern " C " int WINAPI WinMain ( HINSTANCE hInstance ,
HINSTANCE hPrevInstance ,
wxCmdLineArgType WXUNUSED ( lpCmdLine ) ,
int nCmdShow )
{
wxDISABLE_DEBUG_SUPPORT ( ) ;
// Disable setting of HiDPI aware mode
wxMSWDisableSettingHighDPIAware ( ) ;
/* NB: We pass NULL in place of lpCmdLine to behave the same as */
/* Borland-specific wWinMain() above. If it becomes needed */
/* to pass lpCmdLine to wxEntry() here, you'll have to fix */
/* wWinMain() above too. */
return wxEntry ( hInstance , hPrevInstance , NULL , nCmdShow ) ;
}
2015-07-30 07:02:59 +00:00
# else
IMPLEMENT_APP ( AudacityApp )
2015-07-15 04:33:53 +00:00
# endif
# ifdef __WXMAC__
// in response of an open-document apple event
void AudacityApp : : MacOpenFile ( const wxString & fileName )
{
2019-02-12 00:10:48 +00:00
ofqueue . push_back ( fileName ) ;
2015-07-15 04:33:53 +00:00
}
// in response of a print-document apple event
void AudacityApp : : MacPrintFile ( const wxString & fileName )
{
2019-02-12 00:10:48 +00:00
ofqueue . push_back ( fileName ) ;
2015-07-15 04:33:53 +00:00
}
// 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.
2016-08-14 15:41:59 +00:00
if ( gAudacityProjects . size ( ) = = 0 ) {
2015-07-15 04:33:53 +00:00
CreateNewAudacityProject ( ) ;
}
}
# endif //__WXMAC__
# 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 )
2016-10-03 18:29:42 +00:00
EVT_QUERY_END_SESSION ( AudacityApp : : OnQueryEndSession )
EVT_END_SESSION ( AudacityApp : : OnEndSession )
2015-07-15 04:33:53 +00:00
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 )
2016-04-20 19:34:49 +00:00
// Global ESC key handling
EVT_KEY_DOWN ( AudacityApp : : OnKeyDown )
2015-07-15 04:33:53 +00:00
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.
2016-02-23 02:18:11 +00:00
bool AudacityApp : : MRUOpen ( const wxString & fullPathStr ) {
2015-07-15 04:33:53 +00:00
// Most of the checks below are copied from AudacityProject::OpenFiles.
// - some rationalisation might be possible.
AudacityProject * proj = GetActiveProject ( ) ;
2019-02-12 00:10:48 +00:00
if ( ! fullPathStr . empty ( ) )
2015-07-15 04:33:53 +00:00
{
// verify that the file exists
if ( wxFile : : Exists ( fullPathStr ) )
{
2017-08-03 11:55:51 +00:00
FileNames : : UpdateDefaultPath ( FileNames : : Operation : : Open , fullPathStr ) ;
2015-07-15 04:33:53 +00:00
// 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
2016-02-13 15:43:16 +00:00
// 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
2015-07-15 04:33:53 +00:00
// hasn't done any action at all is it safe for Open to take place
// inside the current project.
//
2016-02-13 15:43:16 +00:00
// If you try to Open a NEW project inside the current window when
2015-07-15 04:33:53 +00:00
// there are no tracks, but there's an Undo history, etc, then
2016-02-13 15:43:16 +00:00
// bad things can happen, including data files moving to the NEW
2015-07-15 04:33:53 +00:00
// project directory, etc.
2016-12-20 18:13:00 +00:00
if ( proj & & ( proj - > GetDirty ( ) | | ! proj - > GetIsEmpty ( ) ) )
proj = nullptr ;
2015-07-15 04:33:53 +00:00
// This project is clean; it's never been touched. Therefore
// all relevant member variables are in their initial state,
2016-02-13 15:43:16 +00:00
// and it's okay to open a NEW project inside this window.
2016-12-20 18:13:00 +00:00
AudacityProject : : OpenProject ( proj , fullPathStr ) ;
2015-07-15 04:33:53 +00:00
}
else {
// File doesn't exist - remove file from history
2017-09-06 21:39:33 +00:00
AudacityMessageBox ( wxString : : Format ( _ ( " %s could not be found. \n \n It has been removed from the list of recent files. " ) ,
2017-10-09 05:03:14 +00:00
fullPathStr ) ) ;
2015-07-15 04:33:53 +00:00
return ( false ) ;
}
}
return ( true ) ;
}
2016-12-20 21:15:20 +00:00
bool AudacityApp : : SafeMRUOpen ( const wxString & fullPathStr )
{
return GuardedCall < bool > ( [ & ] { return MRUOpen ( fullPathStr ) ; } ) ;
}
2015-07-15 04:33:53 +00:00
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 ;
2016-02-22 05:17:20 +00:00
const wxString & fullPathStr = mRecentFiles - > GetHistoryFile ( n ) ;
2015-07-15 04:33:53 +00:00
// 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.
2016-12-20 21:15:20 +00:00
// PRL: Don't call SafeMRUOpen
// -- if open fails for some exceptional reason of resource exhaustion that
// the user can correct, leave the file in history.
2015-07-15 04:33:53 +00:00
if ( ! AudacityProject : : IsAlreadyOpen ( fullPathStr ) & & ! MRUOpen ( fullPathStr ) )
mRecentFiles - > RemoveFileFromHistory ( n ) ;
}
void AudacityApp : : OnTimer ( wxTimerEvent & WXUNUSED ( event ) )
{
2016-12-20 21:15:20 +00:00
// Filenames are queued when Audacity receives a few of the
2015-07-15 04:33:53 +00:00
// AppleEvent messages (via wxWidgets). So, open any that are
// in the queue and clean the queue.
if ( gInited ) {
2019-02-12 00:10:48 +00:00
if ( ofqueue . size ( ) ) {
2015-07-15 04:33:53 +00:00
// Load each file on the queue
2019-02-12 00:10:48 +00:00
while ( ofqueue . size ( ) ) {
2016-02-22 05:17:20 +00:00
wxString name ;
name . swap ( ofqueue [ 0 ] ) ;
2019-02-28 13:54:36 +00:00
ofqueue . erase ( ofqueue . begin ( ) ) ;
2015-07-15 04:33:53 +00:00
// Get the user's attention if no file name was specified
2019-02-12 00:10:48 +00:00
if ( name . empty ( ) ) {
2015-07-15 04:33:53 +00:00
// 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.
2016-12-20 21:15:20 +00:00
// PRL: Catch any exceptions, don't try this file again, continue to
// other files.
if ( ! SafeMRUOpen ( name ) ) {
2015-07-15 04:33:53 +00:00
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.
2016-09-11 19:30:06 +00:00
//size_t numProjects = gAudacityProjects.size();
2016-12-20 21:15:45 +00:00
AProjectHolder offendingProject ;
2015-07-15 04:33:53 +00:00
wxString missingFileName ;
2016-08-15 14:51:16 +00:00
{
ODLocker locker { & m_LastMissingBlockFileLock } ;
2016-12-20 21:15:45 +00:00
offendingProject = m_LastMissingBlockFileProject . lock ( ) ;
2016-08-15 14:51:16 +00:00
missingFileName = m_LastMissingBlockFilePath ;
2015-07-15 04:33:53 +00:00
}
// 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 \
2018-01-17 13:27:00 +00:00
Choose Help > Diagnostics > Check Dependencies to view a list of \
2017-10-09 05:03:14 +00:00
locations of the missing files . " ), missingFileName);
2015-07-15 04:33:53 +00:00
// if an old dialog exists, raise it if it is
if ( offendingProject - > GetMissingAliasFileDialog ( ) ) {
offendingProject - > GetMissingAliasFileDialog ( ) - > Raise ( ) ;
} else {
2016-12-20 21:15:45 +00:00
ShowAliasMissingDialog ( offendingProject . get ( ) , _ ( " Files Missing " ) ,
2015-07-15 04:33:53 +00:00
errorMessage , wxT ( " " ) , true ) ;
}
}
// Only show this warning once per event (playback/menu item/etc).
SetMissingAliasedFileWarningShouldShow ( false ) ;
}
}
2016-08-15 14:51:16 +00:00
void AudacityApp : : MarkAliasedFilesMissingWarning ( const AliasBlockFile * b )
2015-07-15 04:33:53 +00:00
{
2016-08-15 14:51:16 +00:00
ODLocker locker { & m_LastMissingBlockFileLock } ;
if ( b ) {
size_t numProjects = gAudacityProjects . size ( ) ;
for ( size_t ii = 0 ; ii < numProjects ; + + ii ) {
// search each project for the blockfile
if ( gAudacityProjects [ ii ] - > GetDirManager ( ) - > ContainsBlockFile ( b ) ) {
m_LastMissingBlockFileProject = gAudacityProjects [ ii ] ;
break ;
}
}
}
else
m_LastMissingBlockFileProject = { } ;
2015-07-15 04:33:53 +00:00
2016-08-15 14:51:16 +00:00
if ( b )
m_LastMissingBlockFilePath = b - > GetAliasedFileName ( ) . GetFullPath ( ) ;
else
m_LastMissingBlockFilePath = wxString { } ;
2015-07-15 04:33:53 +00:00
}
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 ) {
2016-08-15 14:51:16 +00:00
MarkAliasedFilesMissingWarning ( nullptr ) ;
2015-07-15 04:33:53 +00:00
}
}
bool AudacityApp : : ShouldShowMissingAliasedFileWarning ( )
{
2016-08-15 14:51:16 +00:00
ODLocker locker { & m_LastMissingBlockFileLock } ;
auto ptr = m_LastMissingBlockFileProject . lock ( ) ;
return ptr & & m_aliasMissingWarningShouldShow ;
2015-07-15 04:33:53 +00:00
}
AudacityLogger * AudacityApp : : GetLogger ( )
{
// Use dynamic_cast so that we get a NULL ptr if we haven't yet
// setup our logger.
return dynamic_cast < AudacityLogger * > ( wxLog : : GetActiveTarget ( ) ) ;
}
# if defined(__WXMSW__)
# define WL(lang, sublang) (lang), (sublang),
# else
# define WL(lang,sublang)
# endif
2017-09-25 07:06:24 +00:00
# if wxCHECK_VERSION(3, 0, 1)
2015-07-15 04:33:53 +00:00
wxLanguageInfo userLangs [ ] =
{
2017-09-25 07:06:24 +00:00
// Bosnian is defined in wxWidgets already
// { wxLANGUAGE_USER_DEFINED, wxT("bs"), WL(0, SUBLANG_DEFAULT) wxT("Bosnian"), wxLayout_LeftToRight },
{ wxLANGUAGE_USER_DEFINED , wxT ( " eu " ) , WL ( 0 , SUBLANG_DEFAULT ) wxT ( " Basque " ) , wxLayout_LeftToRight } ,
2015-07-15 04:33:53 +00:00
} ;
# endif
2016-01-27 01:09:54 +00:00
wxString AudacityApp : : InitLang ( const wxString & lang )
2015-07-15 04:33:53 +00:00
{
2016-01-27 01:09:54 +00:00
wxString result = lang ;
2016-04-08 08:32:11 +00:00
mLocale . reset ( ) ;
2015-07-15 04:33:53 +00:00
# if defined(__WXMAC__)
// This should be reviewed again during the wx3 conversion.
// On OSX, if the LANG environment variable isn't set when
// using a language like Japanese, an assertion will trigger
// because conversion to Japanese from "?" doesn't return a
// valid length, so make OSX happy by defining/overriding
// the LANG environment variable with U.S. English for now.
wxSetEnv ( wxT ( " LANG " ) , wxT ( " en_US.UTF-8 " ) ) ;
# endif
2016-01-27 01:09:54 +00:00
const wxLanguageInfo * info = NULL ;
if ( ! lang . empty ( ) ) {
info = wxLocale : : FindLanguageInfo ( lang ) ;
if ( ! info )
2017-09-06 21:39:33 +00:00
: : AudacityMessageBox ( wxString : : Format ( _ ( " Language \" %s \" is unknown " ) , lang ) ) ;
2016-01-27 01:09:54 +00:00
}
if ( ! info )
2015-07-28 20:23:30 +00:00
{
2016-01-27 01:09:54 +00:00
result = GetSystemLanguageCode ( ) ;
info = wxLocale : : FindLanguageInfo ( result ) ;
if ( ! info )
return result ;
2015-07-28 20:23:30 +00:00
}
2016-04-08 08:32:11 +00:00
mLocale = std : : make_unique < wxLocale > ( info - > Language ) ;
2015-07-15 04:33:53 +00:00
2019-02-12 00:10:48 +00:00
for ( unsigned int i = 0 ; i < audacityPathList . size ( ) ; i + + )
2015-07-15 04:33:53 +00:00
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.
2015-07-28 20:06:25 +00:00
2015-07-15 04:33:53 +00:00
Internat : : Init ( ) ;
2015-07-28 20:06:25 +00:00
2015-08-17 15:49:13 +00:00
// Notify listeners of language changes
{
wxCommandEvent evt ( EVT_LANGUAGE_CHANGE ) ;
ProcessEvent ( evt ) ;
}
2016-01-27 01:09:54 +00:00
2017-09-30 00:14:37 +00:00
// PRL: Moved this, do it only after language intialized
// Unused strings that we want to be translated, even though
// we're not using them yet...
wxString future1 = _ ( " Master Gain Control " ) ;
2016-01-27 01:09:54 +00:00
return result ;
2015-07-15 04:33:53 +00:00
}
void AudacityApp : : OnFatalException ( )
{
# if defined(EXPERIMENTAL_CRASH_REPORT)
GenerateCrashReport ( wxDebugReport : : Context_Exception ) ;
# endif
exit ( - 1 ) ;
}
2018-01-06 13:03:48 +00:00
2018-02-15 12:31:50 +00:00
# ifdef _MSC_VER
// If this is compiled with MSVC (Visual Studio)
2018-01-06 13:03:48 +00:00
# pragma warning( push )
# pragma warning( disable : 4702) // unreachable code warning.
2018-02-15 12:31:50 +00:00
# endif //_MSC_VER
2018-01-06 13:03:48 +00:00
2016-11-08 15:10:50 +00:00
bool AudacityApp : : OnExceptionInMainLoop ( )
{
// This function is invoked from catch blocks in the wxWidgets framework,
// and throw; without argument re-throws the exception being handled,
// letting us dispatch according to its type.
try { throw ; }
catch ( AudacityException & e ) {
2018-10-10 18:44:31 +00:00
( void ) e ; // Compiler food
2016-11-08 15:10:50 +00:00
// Here is the catch-all for our own exceptions
// Use CallAfter to delay this to the next pass of the event loop,
// rather than risk doing it inside stack unwinding.
auto pProject = : : GetActiveProject ( ) ;
2018-04-16 18:16:11 +00:00
auto pException = std : : current_exception ( ) ;
2016-11-08 15:10:50 +00:00
CallAfter ( [ = ] // Capture pException by value!
{
// Restore the state of the project to what it was before the
// failed operation
2019-01-28 17:50:20 +00:00
if ( pProject ) {
pProject - > RollbackState ( ) ;
2016-11-08 15:10:50 +00:00
2019-01-28 17:50:20 +00:00
// Forget pending changes in the TrackList
pProject - > GetTracks ( ) - > ClearPendingTracks ( ) ;
2017-08-24 18:31:51 +00:00
2019-01-28 17:50:20 +00:00
pProject - > RedrawProject ( ) ;
}
2016-11-08 15:10:50 +00:00
// Give the user an alert
2018-04-16 18:16:11 +00:00
try { std : : rethrow_exception ( pException ) ; }
catch ( AudacityException & e )
{ e . DelayedHandlerAction ( ) ; }
2016-11-08 15:10:50 +00:00
} ) ;
// Don't quit the program
return true ;
}
catch ( . . . ) {
// There was some other type of exception we don't know.
// Let the inherited function do throw; again and whatever else it does.
return wxApp : : OnExceptionInMainLoop ( ) ;
}
// Shouldn't ever reach this line
return false ;
}
2018-02-15 12:31:50 +00:00
# ifdef _MSC_VER
2018-01-06 13:03:48 +00:00
# pragma warning( pop )
2018-02-15 12:31:50 +00:00
# endif //_MSC_VER
2016-11-08 15:10:50 +00:00
2015-07-15 04:33:53 +00:00
# if defined(EXPERIMENTAL_CRASH_REPORT)
void AudacityApp : : GenerateCrashReport ( wxDebugReport : : Context ctx )
{
wxDebugReportCompress rpt ;
rpt . AddAll ( ctx ) ;
wxFileName fn ( FileNames : : DataDir ( ) , wxT ( " audacity.cfg " ) ) ;
2016-09-11 12:03:37 +00:00
rpt . AddFile ( fn . GetFullPath ( ) , _TS ( " Audacity Configuration " ) ) ;
2015-07-15 04:33:53 +00:00
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 " ) ) ;
2017-06-01 15:51:09 +00:00
# ifdef EXPERIMENTAL_MIDI_OUT
rpt . AddText ( wxT ( " mididev.txt " ) , gAudioIO - > GetMidiDeviceInfo ( ) , wxT ( " MIDI Device Info " ) ) ;
# endif
2015-07-15 04:33:53 +00:00
}
AudacityLogger * logger = GetLogger ( ) ;
if ( logger )
{
2016-09-11 12:03:37 +00:00
rpt . AddText ( wxT ( " log.txt " ) , logger - > GetLog ( ) , _TS ( " Audacity Log " ) ) ;
2015-07-15 04:33:53 +00:00
}
bool ok = wxDebugReportPreviewStd ( ) . Show ( rpt ) ;
# if defined(__WXMSW__)
wxEventLoop : : SetCriticalWindow ( NULL ) ;
# endif
if ( ok & & rpt . Process ( ) )
{
2017-10-12 15:49:57 +00:00
AudacityTextEntryDialog dlg ( NULL ,
2015-07-15 04:33:53 +00:00
_ ( " Report generated to: " ) ,
_ ( " Audacity Support Data " ) ,
rpt . GetCompressedFileName ( ) ,
wxOK | wxCENTER ) ;
dlg . SetName ( dlg . GetTitle ( ) ) ;
dlg . ShowModal ( ) ;
wxLogMessage ( wxT ( " Report generated to: %s " ) ,
2017-10-09 05:03:14 +00:00
rpt . GetCompressedFileName ( ) ) ;
2015-07-15 04:33:53 +00:00
rpt . Reset ( ) ;
}
}
# endif
int AudacityApp : : FilterEvent ( wxEvent & event )
{
2016-09-11 19:30:06 +00:00
( void ) event ; // compiler food (stops unused parameter warning)
2015-07-28 20:23:30 +00:00
# if !wxCHECK_VERSION(3, 0, 0) && 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
//
2015-07-15 04:33:53 +00:00
if ( event . GetEventType ( ) = = wxEVT_ACTIVATE )
{
wxActivateEvent & e = ( wxActivateEvent & ) event ;
if ( e . GetEventObject ( ) & & e . GetActive ( ) & & e . GetEventObject ( ) - > IsKindOf ( CLASSINFO ( wxDialog ) ) )
{
( ( wxWindow * ) e . GetEventObject ( ) ) - > SetFocus ( ) ;
}
}
2015-07-28 20:23:30 +00:00
# endif
2015-07-15 04:33:53 +00:00
2016-09-22 17:56:26 +00:00
# if defined(__WXMAC__) || defined(__WXGTK__)
2016-06-21 22:29:00 +00:00
if ( event . GetEventType ( ) = = wxEVT_ACTIVATE )
{
wxActivateEvent & e = static_cast < wxActivateEvent & > ( event ) ;
const auto object = e . GetEventObject ( ) ;
if ( object & & e . GetActive ( ) & &
object - > IsKindOf ( CLASSINFO ( wxWindow ) ) )
{
const auto window = ( ( wxWindow * ) e . GetEventObject ( ) ) ;
2017-07-23 16:39:51 +00:00
window - > SetFocus ( ) ;
2016-12-06 12:27:42 +00:00
# if defined(__WXMAC__)
2017-07-23 16:39:51 +00:00
window - > NavigateIn ( ) ;
2016-12-06 12:27:42 +00:00
# endif
2016-06-21 22:29:00 +00:00
}
}
# endif
2015-07-28 20:23:30 +00:00
return Event_Skip ;
2015-07-15 04:33:53 +00:00
}
AudacityApp : : AudacityApp ( )
{
// Do not capture crashes in debug builds
# if !defined(__WXDEBUG__)
# if defined(EXPERIMENTAL_CRASH_REPORT)
# if defined(wxUSE_ON_FATAL_EXCEPTION) && wxUSE_ON_FATAL_EXCEPTION
wxHandleFatalExceptions ( ) ;
# endif
# endif
# endif
}
2016-04-10 03:02:25 +00:00
AudacityApp : : ~ AudacityApp ( )
{
}
2015-07-15 04:33:53 +00:00
// The `main program' equivalent, creating the windows and returning the
// main frame
bool AudacityApp : : OnInit ( )
{
2016-07-09 21:36:48 +00:00
// JKC: ANSWER-ME: Who actually added the event loop guarantor?
// Although 'blame' says Leland, I think it came from a donated patch.
2016-12-26 14:03:14 +00:00
// PRL: It was added by LL at 54676a72285ba7ee3a69920e91fa390a71ef10c9 :
// " Ensure OnInit() has an event loop
// And allow events to flow so the splash window updates under GTK"
// then mistakenly lost in the merge at
// 37168ebbf67ae869ab71a3b5cbbf1d2a48e824aa
// then restored at 7687972aa4b2199f0717165235f3ef68ade71e08
2015-07-28 20:23:30 +00:00
// Ensure we have an event loop during initialization
wxEventLoopGuarantor eventLoop ;
2016-08-13 01:15:50 +00:00
// wxWidgets will clean up the logger for the main thread, so we can say
// safenew. See:
// http://docs.wxwidgets.org/3.0/classwx_log.html#a2525bf54fa3f31dc50e6e3cd8651e71d
std : : unique_ptr < wxLog >
{ wxLog : : SetActiveTarget ( safenew AudacityLogger ) } ; // DELETE old
2015-07-15 04:33:53 +00:00
mLocale = NULL ;
m_aliasMissingWarningShouldShow = true ;
# if defined(__WXMAC__)
// Disable window animation
2016-06-26 02:17:24 +00:00
wxSystemOptions : : SetOption ( wxMAC_WINDOW_PLAIN_TRANSITION , 1 ) ;
2015-07-15 04:33:53 +00:00
# endif
2017-01-04 14:46:15 +00:00
// Don't use AUDACITY_NAME here.
// We want Audacity with a capital 'A'
2017-04-02 22:07:13 +00:00
// DA: App name
# ifndef EXPERIMENTAL_DA
wxString appName = wxT ( " Audacity " ) ;
# else
wxString appName = wxT ( " DarkAudacity " ) ;
# endif
2015-07-15 04:33:53 +00:00
wxTheApp - > SetAppName ( appName ) ;
2015-08-16 11:18:41 +00:00
// Explicitly set since OSX will use it for the "Quit" menu item
2016-09-11 12:03:37 +00:00
wxTheApp - > SetAppDisplayName ( appName ) ;
wxTheApp - > SetVendorName ( appName ) ;
2015-07-15 04:33:53 +00:00
: : wxInitAllImageHandlers ( ) ;
2016-08-10 03:40:11 +00:00
// AddHandler takes ownership
wxFileSystem : : AddHandler ( safenew wxZipFSHandler ) ;
2015-07-15 04:33:53 +00:00
//
// Paths: set search path and temp dir path
//
# ifdef __WXGTK__
/* 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 */
wxString home = wxGetHomeDir ( ) ;
2017-11-07 16:07:04 +00:00
wxString envTempDir = wxGetenv ( wxT ( " TMPDIR " ) ) ;
2019-03-14 17:04:37 +00:00
if ( ! envTempDir . empty ( ) ) {
2017-11-07 16:07:04 +00:00
/* On Unix systems, the environment variable TMPDIR may point to
an unusual path when / tmp and / var / tmp are not desirable . */
defaultTempDir . Printf ( wxT ( " %s/audacity-%s " ) , envTempDir , wxGetUserId ( ) ) ;
} else {
/* On Unix systems, the default temp dir is in /var/tmp. */
defaultTempDir . Printf ( wxT ( " /var/tmp/audacity-%s " ) , wxGetUserId ( ) ) ;
}
2015-07-15 04:33:53 +00:00
2017-04-02 22:07:13 +00:00
// DA: Path env variable.
# ifndef EXPERIMENTAL_DA
2015-07-15 04:33:53 +00:00
wxString pathVar = wxGetenv ( wxT ( " AUDACITY_PATH " ) ) ;
2017-04-02 22:07:13 +00:00
# else
wxString pathVar = wxGetenv ( wxT ( " DARKAUDACITY_PATH " ) ) ;
# endif
2019-03-14 17:04:37 +00:00
if ( ! pathVar . empty ( ) )
2015-07-15 04:33:53 +00:00
AddMultiPathsToPathList ( pathVar , audacityPathList ) ;
AddUniquePathToPathList ( : : wxGetCwd ( ) , audacityPathList ) ;
# ifdef AUDACITY_NAME
AddUniquePathToPathList ( wxString : : Format ( wxT ( " %s/.%s-files " ) ,
2017-10-09 05:03:14 +00:00
home , wxT ( AUDACITY_NAME ) ) ,
2016-02-01 01:39:24 +00:00
audacityPathList ) ;
2015-07-15 04:33:53 +00:00
AddUniquePathToPathList ( wxString : : Format ( wxT ( " %s/share/%s " ) ,
2016-02-01 01:39:24 +00:00
wxT ( INSTALL_PREFIX ) , wxT ( AUDACITY_NAME ) ) ,
audacityPathList ) ;
2015-07-15 04:33:53 +00:00
AddUniquePathToPathList ( wxString : : Format ( wxT ( " %s/share/doc/%s " ) ,
2016-02-01 01:39:24 +00:00
wxT ( INSTALL_PREFIX ) , wxT ( AUDACITY_NAME ) ) ,
audacityPathList ) ;
2015-07-15 04:33:53 +00:00
# else //AUDACITY_NAME
AddUniquePathToPathList ( wxString : : Format ( wxT ( " %s/.audacity-files " ) ,
2017-10-09 05:03:14 +00:00
home ) ,
2016-02-01 01:39:24 +00:00
audacityPathList ) ;
2015-07-15 04:33:53 +00:00
AddUniquePathToPathList ( wxString : : Format ( wxT ( " %s/share/audacity " ) ,
2016-02-01 01:39:24 +00:00
wxT ( INSTALL_PREFIX ) ) ,
audacityPathList ) ;
2015-07-15 04:33:53 +00:00
AddUniquePathToPathList ( wxString : : Format ( wxT ( " %s/share/doc/audacity " ) ,
2016-02-01 01:39:24 +00:00
wxT ( INSTALL_PREFIX ) ) ,
audacityPathList ) ;
2015-07-15 04:33:53 +00:00
# endif //AUDACITY_NAME
AddUniquePathToPathList ( wxString : : Format ( wxT ( " %s/share/locale " ) ,
2016-02-01 01:39:24 +00:00
wxT ( INSTALL_PREFIX ) ) ,
audacityPathList ) ;
2015-07-15 04:33:53 +00:00
AddUniquePathToPathList ( wxString : : Format ( wxT ( " ./locale " ) ) ,
2016-02-01 01:39:24 +00:00
audacityPathList ) ;
2015-07-15 04:33:53 +00:00
# endif //__WXGTK__
2016-07-06 11:10:26 +00:00
// JKC Bug 1220: Use path based on home directory on WXMAC
# ifdef __WXMAC__
wxFileName tmpFile ;
tmpFile . AssignHomeDir ( ) ;
wxString tmpDirLoc = tmpFile . GetPath ( wxPATH_GET_VOLUME ) ;
# else
2015-07-15 04:33:53 +00:00
wxFileName tmpFile ;
tmpFile . AssignTempFileName ( wxT ( " nn " ) ) ;
wxString tmpDirLoc = tmpFile . GetPath ( wxPATH_GET_VOLUME ) ;
: : wxRemoveFile ( tmpFile . GetFullPath ( ) ) ;
2016-07-06 11:10:26 +00:00
# endif
2015-07-15 04:33:53 +00:00
// 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 ) ;
2016-02-01 01:39:24 +00:00
AddUniquePathToPathList ( progPath + wxT ( " \\ Languages " ) , audacityPathList ) ;
2015-07-15 04:33:53 +00:00
2015-12-22 01:32:43 +00:00
// See bug #1271 for explanation of location
tmpDirLoc = FileNames : : MkDir ( wxStandardPaths : : Get ( ) . GetUserLocalDataDir ( ) ) ;
2015-12-23 18:39:26 +00:00
defaultTempDir . Printf ( wxT ( " %s \\ SessionData " ) ,
2017-10-09 05:03:14 +00:00
tmpDirLoc ) ;
2015-07-15 04:33:53 +00:00
# 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.
2017-01-03 15:35:22 +00:00
//AddUniquePathToPathList(progPath + wxT("/../../../"), audacityPathList);
2015-07-15 04:33:53 +00:00
// These allow for searching the "bundle"
2016-02-01 01:39:24 +00:00
AddUniquePathToPathList ( progPath + wxT ( " /../ " ) , audacityPathList ) ;
AddUniquePathToPathList ( progPath + wxT ( " /../Resources " ) , audacityPathList ) ;
2015-07-15 04:33:53 +00:00
2016-07-06 11:10:26 +00:00
// JKC Bug 1220: Using an actual temp directory for session data on Mac was
// wrong because it would get cleared out on a reboot.
2016-09-22 18:29:52 +00:00
defaultTempDir . Printf ( wxT ( " %s/Library/Application Support/audacity/SessionData " ) ,
2017-10-09 05:03:14 +00:00
tmpDirLoc ) ;
2016-07-06 11:10:26 +00:00
//defaultTempDir.Printf(wxT("%s/audacity-%s"),
2017-10-09 05:03:14 +00:00
// tmpDirLoc,
// wxGetUserId());
2015-07-15 04:33:53 +00:00
# endif //__WXMAC__
// Define languanges for which we have translations, but that are not yet
// supported by wxWidgets.
//
// TODO: The whole Language initialization really need to be reworked.
// It's all over the place.
2017-09-25 07:06:24 +00:00
# if wxCHECK_VERSION(3, 0, 1)
2015-07-15 04:33:53 +00:00
for ( size_t i = 0 , cnt = WXSIZEOF ( userLangs ) ; i < cnt ; i + + )
{
wxLocale : : AddLanguage ( userLangs [ i ] ) ;
}
# endif
// Initialize preferences and language
InitPreferences ( ) ;
2016-02-01 01:39:24 +00:00
# if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
this - > AssociateFileTypes ( ) ;
# endif
2015-07-15 04:33:53 +00:00
// TODO - read the number of files to store in history from preferences
2016-04-08 08:32:11 +00:00
mRecentFiles = std : : make_unique < FileHistory > ( ID_RECENT_LAST - ID_RECENT_FIRST + 1 , ID_RECENT_CLEAR ) ;
2015-07-15 04:33:53 +00:00
mRecentFiles - > Load ( * gPrefs , wxT ( " RecentFiles " ) ) ;
theTheme . EnsureInitialised ( ) ;
// AColor depends on theTheme.
AColor : : Init ( ) ;
// Init DirManager, which initializes the temp directory
// If this fails, we must exit the program.
if ( ! InitTempDir ( ) ) {
FinishPreferences ( ) ;
return false ;
}
2016-02-01 01:39:24 +00:00
//<<<< 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.
2015-07-15 04:33:53 +00:00
// Initialize the CommandHandler
InitCommandHandler ( ) ;
// Initialize the PluginManager
PluginManager : : Get ( ) . Initialize ( ) ;
// Initialize the ModuleManager, including loading found modules
ModuleManager : : Get ( ) . Initialize ( * mCmdHandler ) ;
// 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.
2016-12-19 22:56:52 +00:00
std : : shared_ptr < wxCmdLineParser > parser { ParseCommandLine ( ) . release ( ) } ;
2015-07-15 04:33:53 +00:00
if ( ! parser )
{
// Either user requested help or a parsing error occured
exit ( 1 ) ;
}
if ( parser - > Found ( wxT ( " v " ) ) )
{
2018-11-04 17:03:27 +00:00
wxPrintf ( " Audacity v%s \n " , AUDACITY_VERSION_STRING ) ;
2015-07-15 04:33:53 +00:00
exit ( 0 ) ;
}
long lval ;
if ( parser - > Found ( wxT ( " b " ) , & lval ) )
{
if ( lval < 256 | | lval > 100000000 )
{
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 ) ;
}
// BG: Create a temporary window to set as the top window
2016-02-01 01:39:24 +00:00
wxImage logoimage ( ( const char * * ) AudacityLogoWithName_xpm ) ;
2015-07-15 04:33:53 +00:00
logoimage . Rescale ( logoimage . GetWidth ( ) / 2 , logoimage . GetHeight ( ) / 2 ) ;
2018-08-08 14:04:15 +00:00
if ( GetLayoutDirection ( ) = = wxLayout_RightToLeft )
logoimage = logoimage . Mirror ( ) ;
2015-07-15 04:33:53 +00:00
wxBitmap logo ( logoimage ) ;
2016-02-01 01:39:24 +00:00
AudacityProject * project ;
{
2017-05-15 09:45:49 +00:00
// Bug 718: Position splash screen on same screen
// as where Audacity project will appear.
wxRect wndRect ;
bool bMaximized = false ;
bool bIconized = false ;
GetNextWindowPlacement ( & wndRect , & bMaximized , & bIconized ) ;
2016-02-01 01:39:24 +00:00
wxSplashScreen temporarywindow (
logo ,
2017-06-09 13:25:04 +00:00
wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_NO_TIMEOUT ,
2016-02-01 01:39:24 +00:00
0 ,
NULL ,
wxID_ANY ,
2017-06-09 13:25:04 +00:00
wndRect . GetTopLeft ( ) ,
2016-02-01 01:39:24 +00:00
wxDefaultSize ,
wxSTAY_ON_TOP ) ;
2018-11-04 17:03:27 +00:00
2017-06-09 13:25:04 +00:00
// Unfortunately with the Windows 10 Creators update, the splash screen
// now appears before setting its position.
// On a dual monitor screen it will appear on one screen and then
// possibly jump to the second.
2018-08-08 14:04:15 +00:00
// We could fix this by writing our own splash screen and using Hide()
2017-06-09 13:25:04 +00:00
// until the splash scren was correctly positioned, then Show()
2017-05-15 09:45:49 +00:00
// Possibly move it on to the second screen...
temporarywindow . SetPosition ( wndRect . GetTopLeft ( ) ) ;
// Centered on whichever screen it is on.
temporarywindow . Center ( ) ;
2016-02-01 01:39:24 +00:00
temporarywindow . SetTitle ( _ ( " Audacity is starting up... " ) ) ;
SetTopWindow ( & temporarywindow ) ;
2016-07-09 21:36:48 +00:00
// ANSWER-ME: Why is YieldFor needed at all?
//wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI|wxEVT_CATEGORY_USER_INPUT|wxEVT_CATEGORY_UNKNOWN);
2016-02-01 01:39:24 +00:00
wxEventLoopBase : : GetActive ( ) - > YieldFor ( wxEVT_CATEGORY_UI ) ;
//JKC: Would like to put module loading here.
// More initialization
InitDitherers ( ) ;
InitAudioIO ( ) ;
2015-07-15 04:33:53 +00:00
# ifdef __WXMAC__
2016-02-01 01:39:24 +00:00
// 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.
2015-07-15 04:33:53 +00:00
2016-08-10 05:32:13 +00:00
auto fileMenu = std : : make_unique < wxMenu > ( ) ;
auto urecentMenu = std : : make_unique < wxMenu > ( ) ;
auto recentMenu = urecentMenu . get ( ) ;
2016-02-01 01:39:24 +00:00
fileMenu - > Append ( wxID_NEW , wxString ( _ ( " &New " ) ) + wxT ( " \t Ctrl+N " ) ) ;
fileMenu - > Append ( wxID_OPEN , wxString ( _ ( " &Open... " ) ) + wxT ( " \t Ctrl+O " ) ) ;
2016-08-10 05:32:13 +00:00
fileMenu - > AppendSubMenu ( urecentMenu . release ( ) , _ ( " Open &Recent... " ) ) ;
2016-02-01 01:39:24 +00:00
fileMenu - > Append ( wxID_ABOUT , _ ( " &About Audacity... " ) ) ;
fileMenu - > Append ( wxID_PREFERENCES , wxString ( _ ( " &Preferences... " ) ) + wxT ( " \t Ctrl+, " ) ) ;
2015-07-15 04:33:53 +00:00
2016-02-21 01:23:54 +00:00
{
auto menuBar = std : : make_unique < wxMenuBar > ( ) ;
2016-08-10 05:32:13 +00:00
menuBar - > Append ( fileMenu . release ( ) , _ ( " &File " ) ) ;
2015-07-15 04:33:53 +00:00
2016-02-21 01:23:54 +00:00
// PRL: Are we sure wxWindows will not leak this menuBar?
// The online documentation is not explicit.
wxMenuBar : : MacSetCommonMenuBar ( menuBar . release ( ) ) ;
}
2015-07-15 04:33:53 +00:00
2016-02-01 01:39:24 +00:00
mRecentFiles - > UseMenu ( recentMenu ) ;
mRecentFiles - > AddFilesToMenu ( recentMenu ) ;
2015-07-15 04:33:53 +00:00
2016-02-01 01:39:24 +00:00
SetExitOnFrameDelete ( false ) ;
2015-07-15 04:33:53 +00:00
2015-07-20 07:54:13 +00:00
# endif //__WXMAC__
2016-07-09 21:36:48 +00:00
temporarywindow . Show ( false ) ;
}
2015-07-15 04:33:53 +00:00
2016-07-09 21:36:48 +00:00
// Workaround Bug 1377 - Crash after Audacity starts and low disk space warning appears
// The temporary splash window is closed AND cleaned up, before attempting to create
// a project and possibly creating a modal warning dialog by doing so.
// Also fixes problem of warning being obscured.
// Downside is that we have no splash screen for the (brief) time that we spend
// creating the project.
// Root cause is problem with wxSplashScreen and other dialogs co-existing, that
// seemed to arrive with wx3.
{
2016-02-01 01:39:24 +00:00
project = CreateNewAudacityProject ( ) ;
mCmdHandler - > SetProject ( project ) ;
wxWindow * pWnd = MakeHijackPanel ( ) ;
if ( pWnd )
{
project - > Show ( false ) ;
pWnd - > SetParent ( project ) ;
SetTopWindow ( pWnd ) ;
pWnd - > Show ( true ) ;
}
}
2015-07-15 04:33:53 +00:00
2017-02-25 22:30:23 +00:00
if ( project - > mShowSplashScreen ) {
// This may do a check-for-updates at every start up.
// Mainly this is to tell users of ALPHAS who don't know that they have an ALPHA.
2017-02-28 19:05:06 +00:00
// Disabled for now, after discussion.
// project->MayCheckForUpdates();
2018-10-23 21:47:23 +00:00
HelpActions : : DoHelpWelcome ( * project ) ;
2017-02-25 22:30:23 +00:00
}
2015-07-15 04:33:53 +00:00
// 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 ( ) ;
2016-12-19 22:56:52 +00:00
// Bug1561: delay the recovery dialog, to avoid crashes.
CallAfter ( [ = ] ( ) mutable {
//
// Auto-recovery
//
bool didRecoverAnything = false ;
if ( ! ShowAutoRecoveryDialogIfNeeded ( & project , & didRecoverAnything ) )
2015-07-15 04:33:53 +00:00
{
2016-12-19 22:56:52 +00:00
// Important: Prevent deleting any temporary files!
DirManager : : SetDontDeleteTempFiles ( ) ;
QuitAudacity ( true ) ;
2015-07-15 04:33:53 +00:00
}
2016-12-19 22:56:52 +00:00
//
// Remainder of command line parsing, but only if we didn't recover
//
if ( ! didRecoverAnything )
2015-07-15 04:33:53 +00:00
{
2016-12-19 22:56:52 +00:00
if ( parser - > Found ( wxT ( " t " ) ) )
{
RunBenchmark ( NULL ) ;
2016-12-26 01:07:43 +00:00
QuitAudacity ( true ) ;
2016-12-19 22:56:52 +00:00
}
// As of wx3, there's no need to process the filename arguments as they
2018-11-04 17:03:27 +00:00
// will be sent via the MacOpenFile() method.
2016-12-19 22:56:52 +00:00
# if !defined(__WXMAC__)
for ( size_t i = 0 , cnt = parser - > GetParamCount ( ) ; i < cnt ; i + + )
{
2016-12-20 21:15:20 +00:00
// PRL: Catch any exceptions, don't try this file again, continue to
// other files.
SafeMRUOpen ( parser - > GetParam ( i ) ) ;
2016-12-19 22:56:52 +00:00
}
2015-08-16 09:01:49 +00:00
# endif
2016-12-19 22:56:52 +00:00
}
} ) ;
2015-07-15 04:33:53 +00:00
gInited = true ;
ModuleManager : : Get ( ) . Dispatch ( AppInitialized ) ;
mWindowRectAlreadySaved = FALSE ;
mTimer . SetOwner ( this , kAudacityAppTimerID ) ;
mTimer . Start ( 200 ) ;
2015-07-28 20:23:30 +00:00
return TRUE ;
2015-07-15 04:33:53 +00:00
}
void AudacityApp : : InitCommandHandler ( )
{
Automation: AudacityCommand
This is a squash of 50 commits.
This merges the capabilities of BatchCommands and Effects using a new
AudacityCommand class. AudacityCommand provides one function to specify the
parameters, and then we leverage that one function in automation, whether by chains,
mod-script-pipe or (future) Nyquist.
- Now have AudacityCommand which is using the same mechanism as Effect
- Has configurable parameters
- Has data-entry GUI (built using shuttle GUI)
- Registers with PluginManager.
- Menu commands now provided in chains, and to python batch.
- Tested with Zoom Toggle.
- ShuttleParams now can set, get, set defaults, validate and specify
the parameters.
- Bugfix: Don't overwrite values with defaults first time out.
- Add DefineParams function for all built-in effects.
- Extend CommandContext to carry output channels for results.
We abuse EffectsManager. It handles both Effects and
AudacityCommands now. In time an Effect should become a special case of
AudacityCommand and we'll split and rename the EffectManager class.
- Don't use 'default' as a parameter name.
- Massive renaming for CommandDefinitionInterface
- EffectIdentInterface becomes EffectDefinitionInterface
- EffectAutomationParameters becomes CommandAutomationParameters
- PluginType is now a bit field.
This way we can search for related types at the same time.
- Most old batch commands made into AudacityCommands.
The ones that weren't are for a reason. They are used by mod-script-pipe
to carry commands and responses across from a non-GUI thread to the GUI
thread.
- Major tidy up of ScreenshotCommand
- Reworking of SelectCommand
- GetPreferenceCommand and SetPreferenceCommand
- GetTrackInfo and SetTrackInfo
- GetInfoCommand
- Help, Open, Save, Import and Export commands.
- Removed obsolete commands ExecMenu, GetProjectInfo and SetProjectInfo
which are now better handled by other commands.
- JSONify "GetInfo: Commands" output, i.e. commas in the right places.
- General work on better Doxygen.
- Lyrics -> LyricsPanel
- Meter -> MeterPanel
- Updated Linux makefile.
- Scripting commands added into Extra menu.
- Distinct names for previously duplicated find-clipping parameters.
- Fixed longstanding error with erroneous status field number which
previously caused an ASSERT in debug.
- Sensible formatting of numbers in Chains, 0.1 not 0.1000000000137
2018-01-14 18:51:41 +00:00
mCmdHandler = std : : make_unique < CommandHandler > ( ) ;
2015-07-15 04:33:53 +00:00
//SetNextHandler(mCmdHandler);
}
// AppCommandEvent callback - just pass the event on to the CommandHandler
void AudacityApp : : OnReceiveCommand ( AppCommandEvent & event )
{
wxASSERT ( NULL ! = mCmdHandler ) ;
mCmdHandler - > OnReceiveCommand ( event ) ;
}
2016-04-20 19:34:49 +00:00
void AudacityApp : : OnKeyDown ( wxKeyEvent & event )
{
if ( event . GetKeyCode ( ) = = WXK_ESCAPE ) {
// Stop play, including scrub, but not record
auto project = : : GetActiveProject ( ) ;
auto token = project - > GetAudioIOToken ( ) ;
2016-06-06 05:44:33 +00:00
auto & scrubber = project - > GetScrubber ( ) ;
2018-07-29 18:11:17 +00:00
auto scrubbing = scrubber . HasMark ( ) ;
2016-06-06 05:44:33 +00:00
if ( scrubbing )
scrubber . Cancel ( ) ;
2016-04-26 12:36:14 +00:00
if ( ( token > 0 & &
gAudioIO - > IsAudioTokenActive ( token ) & &
gAudioIO - > GetNumCaptureChannels ( ) = = 0 ) | |
2016-06-06 05:44:33 +00:00
scrubbing )
2016-04-20 19:34:49 +00:00
// ESC out of other play (but not record)
2018-10-23 13:30:27 +00:00
TransportActions : : DoStop ( * project ) ;
2016-04-20 19:34:49 +00:00
else
event . Skip ( ) ;
}
else
event . Skip ( ) ;
}
// We now disallow temp directory name that puts it where cleaner apps will
2015-12-26 14:56:47 +00:00
// try to clean out the files.
bool AudacityApp : : IsTempDirectoryNameOK ( const wxString & Name ) {
2019-02-12 00:10:48 +00:00
if ( Name . empty ( ) )
2017-01-03 15:35:22 +00:00
return false ;
2015-12-26 14:56:47 +00:00
wxFileName tmpFile ;
tmpFile . AssignTempFileName ( wxT ( " nn " ) ) ;
// use Long Path to expand out any abbreviated long substrings.
wxString BadPath = tmpFile . GetLongPath ( ) ;
: : wxRemoveFile ( tmpFile . GetFullPath ( ) ) ;
2016-09-01 21:09:37 +00:00
2016-07-06 15:17:40 +00:00
# ifdef __WXMAC__
2016-09-01 21:09:37 +00:00
// This test is to fix bug 1220 on a 1.x to 2.x to 2.1.3 upgrade.
2016-09-03 16:49:57 +00:00
// It is less permissive than we could be as it stops a path
// with this string ANYWHERE within it rather than excluding just
// the paths that the earlier Audacities used to create.
if ( Name . Contains ( " /tmp/ " ) )
2016-09-01 21:09:37 +00:00
return false ;
2016-07-06 15:17:40 +00:00
BadPath = BadPath . BeforeLast ( ' / ' ) + " / " ;
wxFileName cmpFile ( Name ) ;
wxString NameCanonical = cmpFile . GetLongPath ( ) + " / " ;
# else
2015-12-26 14:56:47 +00:00
BadPath = BadPath . BeforeLast ( ' \\ ' ) + " \\ " ;
wxFileName cmpFile ( Name ) ;
wxString NameCanonical = cmpFile . GetLongPath ( ) + " \\ " ;
2016-07-06 15:17:40 +00:00
# endif
2015-12-26 14:56:47 +00:00
return ! ( NameCanonical . StartsWith ( BadPath ) ) ;
}
2017-01-03 15:35:22 +00:00
// Ensures directory is created and puts the name into result.
// result is unchanged if unsuccessful.
void SetToExtantDirectory ( wxString & result , const wxString & dir ) {
// don't allow path of "".
2019-02-12 00:10:48 +00:00
if ( dir . empty ( ) )
2017-01-03 15:35:22 +00:00
return ;
if ( wxDirExists ( dir ) ) {
result = dir ;
return ;
}
// Use '/' so that this works on Mac and Windows alike.
wxFileName name ( dir + " /junkname.cfg " ) ;
if ( name . Mkdir ( wxS_DIR_DEFAULT , wxPATH_MKDIR_FULL ) )
result = dir ;
}
2015-07-15 04:33:53 +00:00
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__
2019-03-06 19:53:39 +00:00
if ( tempFromPrefs . length ( ) > 0 & & tempFromPrefs [ 0 ] ! = wxT ( ' / ' ) )
2015-07-15 04:33:53 +00:00
tempFromPrefs = wxT ( " " ) ;
# endif
// Stop wxWidgets from printing its own error messages
wxLogNull logNo ;
// Try temp dir that was stored in prefs first
2017-01-03 15:35:22 +00:00
if ( IsTempDirectoryNameOK ( tempFromPrefs ) )
SetToExtantDirectory ( temp , tempFromPrefs ) ;
2015-07-15 04:33:53 +00:00
// If that didn't work, try the default location
2019-03-14 17:04:37 +00:00
if ( temp . empty ( ) )
2017-01-03 15:35:22 +00:00
SetToExtantDirectory ( temp , tempDefaultLoc ) ;
2015-07-15 04:33:53 +00:00
// 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
2019-03-14 17:04:37 +00:00
if ( temp . empty ( ) ) {
2015-07-15 04:33:53 +00:00
// Failed
2015-12-26 14:56:47 +00:00
if ( ! IsTempDirectoryNameOK ( tempFromPrefs ) ) {
2017-09-06 21:39:33 +00:00
AudacityMessageBox ( _ ( " Audacity could not find a safe place to store temporary files. \n Audacity needs a place where automatic cleanup programs won't delete the temporary files. \n Please enter an appropriate directory in the preferences dialog. " ) ) ;
2015-12-26 14:56:47 +00:00
} else {
2017-09-06 21:39:33 +00:00
AudacityMessageBox ( _ ( " Audacity could not find a place to store temporary files. \n Please enter an appropriate directory in the preferences dialog. " ) ) ;
2015-12-26 14:56:47 +00:00
}
2015-07-15 04:33:53 +00:00
2015-07-28 20:06:25 +00:00
// Only want one page of the preferences
DirectoriesPrefsFactory directoriesPrefsFactory ;
PrefsDialog : : Factories factories ;
factories . push_back ( & directoriesPrefsFactory ) ;
GlobalPrefsDialog dialog ( NULL , factories ) ;
2015-07-15 04:33:53 +00:00
dialog . ShowModal ( ) ;
2017-09-06 21:39:33 +00:00
AudacityMessageBox ( _ ( " Audacity is now going to exit. Please launch Audacity again to use the new temporary directory. " ) ) ;
2015-07-15 04:33:53 +00:00
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).
2016-02-23 02:18:11 +00:00
bool AudacityApp : : CreateSingleInstanceChecker ( const wxString & dir )
2015-07-15 04:33:53 +00:00
{
2017-10-09 05:03:14 +00:00
wxString name = wxString : : Format ( wxT ( " audacity-lock-%s " ) , wxGetUserId ( ) ) ;
2016-04-08 08:32:11 +00:00
mChecker . reset ( ) ;
auto checker = std : : make_unique < wxSingleInstanceChecker > ( ) ;
2015-07-15 04:33:53 +00:00
# if defined(__UNIX__)
2015-08-16 03:37:12 +00:00
wxString sockFile ( dir + wxT ( " /.audacity.sock " ) ) ;
2015-07-15 04:33:53 +00:00
# endif
wxString runningTwoCopiesStr = _ ( " Running two copies of Audacity simultaneously may cause \n data loss or cause your system to crash. \n \n " ) ;
2016-04-08 08:32:11 +00:00
if ( ! checker - > Create ( name , dir ) ) {
2015-07-15 04:33:53 +00:00
// 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. \n This folder may be in use by another copy of Audacity. \n " ) +
runningTwoCopiesStr +
_ ( " Do you still want to start Audacity? " ) ;
2017-09-06 21:39:33 +00:00
int action = AudacityMessageBox ( prompt ,
2015-07-15 04:33:53 +00:00
_ ( " Error Locking Temporary Folder " ) ,
wxYES_NO | wxICON_EXCLAMATION ,
NULL ) ;
2016-04-08 08:32:11 +00:00
if ( action = = wxNO )
2015-07-15 04:33:53 +00:00
return false ;
}
2016-04-08 08:32:11 +00:00
else if ( checker - > IsAnotherRunning ( ) ) {
2015-07-15 04:33:53 +00:00
// Parse the command line to ensure correct syntax, but
2018-11-04 17:03:27 +00:00
// ignore options other than -v, and only use the filenames, if any.
2016-04-10 03:02:25 +00:00
auto parser = ParseCommandLine ( ) ;
2015-07-15 04:33:53 +00:00
if ( ! parser )
{
// Complaints have already been made
return false ;
}
2018-11-04 17:03:27 +00:00
if ( parser - > Found ( wxT ( " v " ) ) )
{
wxPrintf ( " Audacity v%s \n " , AUDACITY_VERSION_STRING ) ;
return false ;
}
// Windows and Linux require absolute file names as command may
// not come from current working directory.
wxArrayString filenames ;
for ( size_t i = 0 , cnt = parser - > GetParamCount ( ) ; i < cnt ; i + + )
{
wxFileName filename ( parser - > GetParam ( i ) ) ;
if ( filename . MakeAbsolute ( ) )
2019-02-12 00:10:48 +00:00
filenames . push_back ( filename . GetLongPath ( ) ) ;
2018-11-04 17:03:27 +00:00
}
2015-07-15 04:33:53 +00:00
# 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 + + )
{
2016-04-08 08:32:11 +00:00
std : : unique_ptr < wxConnectionBase > conn { client . MakeConnection ( wxEmptyString , IPC_APPL , IPC_TOPIC ) } ;
2015-07-15 04:33:53 +00:00
if ( conn )
{
bool ok = false ;
2019-02-12 00:10:48 +00:00
if ( filenames . size ( ) > 0 )
2015-07-15 04:33:53 +00:00
{
2019-02-12 00:10:48 +00:00
for ( size_t i = 0 , cnt = filenames . size ( ) ; i < cnt ; i + + )
2015-07-15 04:33:53 +00:00
{
2018-11-04 17:03:27 +00:00
ok = conn - > Execute ( filenames [ i ] ) ;
2015-07-15 04:33:53 +00:00
}
2018-11-04 17:03:27 +00:00
}
2015-07-15 04:33:53 +00:00
else
{
// Send an empty string to force existing Audacity to front
ok = conn - > Execute ( wxEmptyString ) ;
}
if ( ok )
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 ) ;
{
2016-08-10 02:46:59 +00:00
// Setup the socket
// A wxSocketClient must not be deleted by us, but rather, let the
// framework do appropriate delayed deletion after Destroy()
Destroy_ptr < wxSocketClient > sock { safenew 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 + + )
2015-07-15 04:33:53 +00:00
{
2016-08-10 02:46:59 +00:00
// Connect to the existing Audacity
sock - > Connect ( addr , true ) ;
if ( sock - > IsConnected ( ) )
2015-07-15 04:33:53 +00:00
{
2019-02-12 00:10:48 +00:00
if ( filenames . size ( ) > 0 )
2018-04-29 20:03:13 +00:00
{
2019-02-12 00:10:48 +00:00
for ( size_t i = 0 , cnt = filenames . size ( ) ; i < cnt ; i + + )
2018-04-29 20:03:13 +00:00
{
2018-11-04 17:03:27 +00:00
const wxString param = filenames [ i ] ;
2019-03-06 19:53:39 +00:00
sock - > WriteMsg ( ( const wxChar * ) param , ( param . length ( ) + 1 ) * sizeof ( wxChar ) ) ;
2018-04-29 20:03:13 +00:00
}
}
else
2016-08-10 02:46:59 +00:00
{
2018-04-29 20:03:13 +00:00
// Send an empty string to force existing Audacity to front
sock - > WriteMsg ( wxEmptyString , sizeof ( wxChar ) ) ;
2016-08-10 02:46:59 +00:00
}
2018-04-29 20:03:13 +00:00
return sock - > Error ( ) ;
2015-07-15 04:33:53 +00:00
}
2016-08-10 02:46:59 +00:00
wxMilliSleep ( 100 ) ;
2015-07-15 04:33:53 +00:00
}
}
# 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 \n process to open multiple projects simultaneously. \n " ) ;
2017-09-06 21:39:33 +00:00
AudacityMessageBox ( prompt , _ ( " Audacity is already running " ) ,
2015-07-15 04:33:53 +00:00
wxOK | wxICON_ERROR ) ;
return false ;
}
# if defined(__WXMSW__)
// Create the DDE IPC server
2016-04-08 08:32:11 +00:00
mIPCServ = std : : make_unique < IPCServ > ( IPC_APPL ) ;
2015-07-15 04:33:53 +00:00
# else
int mask = umask ( 077 ) ;
remove ( OSFILENAME ( sockFile ) ) ;
wxUNIXaddress addr ;
addr . Filename ( sockFile ) ;
2016-04-08 08:32:11 +00:00
mIPCServ = std : : make_unique < wxSocketServer > ( addr , wxSOCKET_NOWAIT ) ;
2015-07-15 04:33:53 +00:00
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
2016-04-08 08:32:11 +00:00
mChecker = std : : move ( checker ) ;
2015-07-15 04:33:53 +00:00
return true ;
}
# if defined(__UNIX__)
2016-09-11 19:30:06 +00:00
void AudacityApp : : OnServerEvent ( wxSocketEvent & /* evt */ )
2015-07-15 04:33:53 +00:00
{
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.
2019-02-12 00:10:48 +00:00
ofqueue . push_back ( name ) ;
2015-07-15 04:33:53 +00:00
}
}
# endif
2016-04-10 03:02:25 +00:00
std : : unique_ptr < wxCmdLineParser > AudacityApp : : ParseCommandLine ( )
2015-07-15 04:33:53 +00:00
{
2016-04-10 03:02:25 +00:00
auto parser = std : : make_unique < wxCmdLineParser > ( argc , argv ) ;
2015-07-15 04:33:53 +00:00
if ( ! parser )
{
2016-04-10 18:15:51 +00:00
return nullptr ;
2015-07-15 04:33:53 +00:00
}
/*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 )
2016-09-15 11:39:46 +00:00
return parser ;
2015-07-15 04:33:53 +00:00
2016-04-10 03:02:25 +00:00
return { } ;
2015-07-15 04:33:53 +00:00
}
// static
2016-02-23 02:18:11 +00:00
void AudacityApp : : AddUniquePathToPathList ( const wxString & pathArg ,
2015-07-15 04:33:53 +00:00
wxArrayString & pathList )
{
2016-02-23 02:18:11 +00:00
wxFileName pathNorm = pathArg ;
2015-07-15 04:33:53 +00:00
pathNorm . Normalize ( ) ;
2016-02-22 05:17:20 +00:00
const wxString newpath { pathNorm . GetFullPath ( ) } ;
2015-07-15 04:33:53 +00:00
2019-02-12 00:10:48 +00:00
for ( unsigned int i = 0 ; i < pathList . size ( ) ; i + + ) {
2016-02-22 05:17:20 +00:00
if ( wxFileName ( newpath ) = = wxFileName ( pathList [ i ] ) )
2015-07-15 04:33:53 +00:00
return ;
}
2019-02-12 00:10:48 +00:00
pathList . push_back ( newpath ) ;
2015-07-15 04:33:53 +00:00
}
// static
2016-02-23 02:18:11 +00:00
void AudacityApp : : AddMultiPathsToPathList ( const wxString & multiPathStringArg ,
2015-07-15 04:33:53 +00:00
wxArrayString & pathList )
{
2016-02-23 02:18:11 +00:00
wxString multiPathString ( multiPathStringArg ) ;
2019-03-14 17:04:37 +00:00
while ( ! multiPathString . empty ( ) ) {
2015-07-15 04:33:53 +00:00
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 ;
2019-03-14 17:04:37 +00:00
if ( pattern . empty ( ) ) {
2015-07-15 04:33:53 +00:00
return ;
}
2016-02-22 05:17:20 +00:00
wxFileName ff ;
2015-07-15 04:33:53 +00:00
2019-02-12 00:10:48 +00:00
for ( size_t i = 0 ; i < pathList . size ( ) ; i + + ) {
2016-02-22 05:17:20 +00:00
ff = pathList [ i ] + wxFILE_SEP_PATH + pattern ;
wxDir : : GetAllFiles ( ff . GetPath ( ) , & results , ff . GetFullName ( ) , flags ) ;
2015-07-15 04:33:53 +00:00
}
}
2016-10-03 18:29:42 +00:00
void AudacityApp : : OnQueryEndSession ( wxCloseEvent & event )
{
bool mustVeto = false ;
# ifdef __WXMAC__
mustVeto = wxDialog : : OSXHasModalDialogsOpen ( ) ;
# endif
if ( mustVeto )
event . Veto ( true ) ;
else
OnEndSession ( event ) ;
}
2015-07-15 04:33:53 +00:00
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.
2016-08-14 15:41:59 +00:00
if ( ! gAudacityProjects . empty ( ) ) {
while ( gAudacityProjects . size ( ) ) {
// Closing the project has side-effect of
// deletion from gAudacityProjects
2015-07-15 04:33:53 +00:00
if ( force ) {
gAudacityProjects [ 0 ] - > Close ( true ) ;
}
else if ( ! gAudacityProjects [ 0 ] - > Close ( ) ) {
gIsQuitting = false ;
event . Veto ( ) ;
break ;
}
}
}
}
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 ( ) ;
}
}
mRecentFiles - > Save ( * gPrefs , wxT ( " RecentFiles " ) ) ;
FinishPreferences ( ) ;
# ifdef USE_FFMPEG
DropFFmpegLibs ( ) ;
# endif
DeinitFFT ( ) ;
2016-08-16 01:19:50 +00:00
DeinitAudioIO ( ) ;
2015-07-15 04:33:53 +00:00
// Terminate the PluginManager (must be done before deleting the locale)
PluginManager : : Get ( ) . Terminate ( ) ;
if ( mIPCServ )
{
# if defined(__UNIX__)
wxUNIXaddress addr ;
if ( mIPCServ - > GetLocal ( addr ) )
{
remove ( OSFILENAME ( addr . Filename ( ) ) ) ;
}
# endif
}
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.)
2016-09-11 19:30:06 +00:00
void AudacityApp : : OnMenuAbout ( wxCommandEvent & /*event*/ )
2015-07-15 04:33:53 +00:00
{
// This function shadows a similar function
2016-06-20 23:12:03 +00:00
// in Menus.cpp, but should only be used on the Mac platform.
2016-06-20 16:41:09 +00:00
# ifdef __WXMAC__
2016-06-20 23:12:03 +00:00
// Modeless dialog, consistent with other Mac applications
// Not more than one at once!
const auto instance = AboutDialog : : ActiveIntance ( ) ;
if ( instance )
instance - > Raise ( ) ;
else
// This dialog deletes itself when dismissed
2016-06-20 16:41:09 +00:00
( safenew AboutDialog { nullptr } ) - > Show ( true ) ;
# else
2016-06-20 23:12:03 +00:00
wxASSERT ( false ) ;
2016-06-20 16:41:09 +00:00
# endif
2015-07-15 04:33:53 +00:00
}
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.
2016-08-14 15:41:59 +00:00
if ( gAudacityProjects . size ( ) = = 0 )
2015-07-15 04:33:53 +00:00
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.
2016-08-14 15:41:59 +00:00
if ( gAudacityProjects . size ( ) = = 0 )
2015-07-15 04:33:53 +00:00
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.
2016-08-14 15:41:59 +00:00
if ( gAudacityProjects . size ( ) = = 0 ) {
2015-07-28 20:06:25 +00:00
GlobalPrefsDialog dialog ( NULL /* parent */ ) ;
2015-07-15 04:33:53 +00:00
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.
2016-08-14 15:41:59 +00:00
// if(gAudacityProjects.size() == 0)
2015-07-15 04:33:53 +00:00
QuitAudacity ( ) ;
// LL: Veto quit if projects are still open. This can happen
// if the user selected Cancel in a Save dialog.
2016-08-14 15:41:59 +00:00
event . Skip ( gAudacityProjects . size ( ) = = 0 ) ;
2015-07-15 04:33:53 +00:00
}
//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 =
2017-09-06 21:39:33 +00:00
AudacityMessageBox (
2015-07-15 04:33:53 +00:00
_ ( " Audacity project (.AUP) files are not currently \n associated with Audacity. \n \n Associate 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 ( ) ;
}
}
2019-02-12 00:10:48 +00:00
if ( root_key . empty ( ) ) {
2015-07-15 04:33:53 +00:00
//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