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 .
*/ /*******************************************************************/
2019-03-23 18:16:05 +00:00
# include "Audacity.h" // This should always be included first; for USE_* macros and __UNIX__
2018-11-10 19:47:12 +00:00
# include "AudacityApp.h"
2018-11-11 02:40:37 +00:00
2018-11-11 17:27:44 +00:00
# include "Experimental.h"
2015-07-15 04:33:53 +00:00
#if 0
// This may be used to debug memory leaks.
// See: Visual Leak Dectector @ http://vld.codeplex.com/
# include <vld.h>
# endif
2019-03-23 18:28:37 +00:00
# include <wx/setup.h> // for wxUSE_* macros
2019-03-28 17:23:26 +00:00
# include <wx/wxcrtvararg.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/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
2019-03-31 19:24:00 +00:00
# if defined(__WXMSW__)
# include <wx/msw/registry.h> // for wxRegKey
# endif
2015-07-15 04:33:53 +00:00
# include "AudacityLogger.h"
# include "AboutDialog.h"
# include "AColor.h"
# include "AudioIO.h"
# include "Benchmark.h"
2019-04-25 20:49:31 +00:00
# include "Clipboard.h"
2019-05-17 20:29:31 +00:00
# include "CrashReport.h"
2015-07-15 04:33:53 +00:00
# include "DirManager.h"
# include "commands/CommandHandler.h"
# include "commands/AppCommandEvent.h"
# include "widgets/ASlider.h"
# include "FFmpeg.h"
2019-07-13 20:32:07 +00:00
//#include "LangChoice.h"
2015-07-15 04:33:53 +00:00
# include "Languages.h"
2018-10-16 20:45:26 +00:00
# include "Menus.h"
2019-04-25 20:34:07 +00:00
# include "MissingAliasFileDialog.h"
2015-07-15 04:33:53 +00:00
# include "PluginManager.h"
# include "Project.h"
2019-05-29 15:37:47 +00:00
# include "ProjectAudioIO.h"
2019-06-25 04:00:09 +00:00
# include "ProjectAudioManager.h"
2019-06-08 20:05:22 +00:00
# include "ProjectFileManager.h"
2019-06-06 13:55:34 +00:00
# include "ProjectHistory.h"
2019-05-29 15:42:31 +00:00
# include "ProjectManager.h"
2019-05-29 15:31:40 +00:00
# include "ProjectSettings.h"
2019-05-29 16:05:22 +00:00
# include "ProjectWindow.h"
2015-07-15 04:33:53 +00:00
# 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"
2019-06-06 15:21:15 +00:00
# include "AutoRecoveryDialog.h"
2015-07-15 04:33:53 +00:00
# include "SplashDialog.h"
# include "FFT.h"
# include "BlockFile.h"
# include "ondemand/ODManager.h"
2019-05-20 18:27:11 +00:00
# include "widgets/AudacityMessageBox.h"
2015-07-28 20:06:25 +00:00
# include "prefs/DirectoriesPrefs.h"
2019-05-11 11:36:02 +00:00
# include "prefs/GUIPrefs.h"
2016-04-26 12:36:14 +00:00
# include "tracks/ui/Scrubbing.h"
2019-03-30 19:22:43 +00:00
# include "widgets/FileHistory.h"
2015-07-15 04:33:53 +00:00
2019-06-10 23:10:07 +00:00
# ifdef EXPERIMENTAL_EASY_CHANGE_KEY_BINDINGS
2019-06-11 02:12:44 +00:00
# include "prefs/KeyConfigPrefs.h"
2019-06-10 23:10:07 +00:00
# endif
2019-03-30 19:19:26 +00:00
//temporarily commented out till it is added to all projects
2015-07-15 04:33:53 +00:00
//#include "Profiler.h"
# include "ModuleManager.h"
# include "import/Import.h"
# 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
////////////////////////////////////////////////////////////
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
2019-05-11 11:36:02 +00:00
namespace {
void PopulatePreferences ( )
{
bool resetPrefs = false ;
wxString langCode = gPrefs - > Read ( wxT ( " /Locale/Language " ) , wxEmptyString ) ;
bool writeLang = false ;
const wxFileName fn (
FileNames : : ResourcesDir ( ) ,
wxT ( " FirstTime.ini " ) ) ;
if ( fn . FileExists ( ) ) // it will exist if the (win) installer put it there
{
const wxString fullPath { fn . GetFullPath ( ) } ;
wxFileConfig ini ( wxEmptyString ,
wxEmptyString ,
fullPath ,
wxEmptyString ,
wxCONFIG_USE_LOCAL_FILE ) ;
wxString lang ;
if ( ini . Read ( wxT ( " /FromInno/Language " ) , & lang ) )
{
// Only change "langCode" if the language was actually specified in the ini file.
langCode = lang ;
writeLang = true ;
// Inno Setup doesn't allow special characters in the Name values, so "0" is used
// to represent the "@" character.
langCode . Replace ( wxT ( " 0 " ) , wxT ( " @ " ) ) ;
}
ini . Read ( wxT ( " /FromInno/ResetPrefs " ) , & resetPrefs , false ) ;
bool gone = wxRemoveFile ( fullPath ) ; // remove FirstTime.ini
if ( ! gone )
{
2019-12-07 19:30:07 +00:00
AudacityMessageBox (
XO ( " Failed to remove %s " ) . Format ( fullPath ) ,
XO ( " Failed! " ) ) ;
2019-05-11 11:36:02 +00:00
}
}
langCode = GUIPrefs : : InitLang ( langCode ) ;
// User requested that the preferences be completely reset
if ( resetPrefs )
{
// pop up a dialogue
2019-12-07 19:30:07 +00:00
auto prompt = XO (
" Reset Preferences? \n \n This is a one-time question, after an 'install' where you asked to have the Preferences reset. " ) ;
int action = AudacityMessageBox (
prompt ,
XO ( " Reset Audacity Preferences " ) ,
wxYES_NO , NULL ) ;
2019-05-11 11:36:02 +00:00
if ( action = = wxYES ) // reset
{
gPrefs - > DeleteAll ( ) ;
writeLang = true ;
}
}
// Save the specified language
if ( writeLang )
{
gPrefs - > Write ( wxT ( " /Locale/Language " ) , langCode ) ;
}
// In AUdacity 2.1.0 support for the legacy 1.2.x preferences (depreciated since Audacity
// 1.3.1) is dropped. As a result we can drop the import flag
// first time this version of Audacity is run we try to migrate
// old preferences.
bool newPrefsInitialized = false ;
gPrefs - > Read ( wxT ( " /NewPrefsInitialized " ) , & newPrefsInitialized , false ) ;
if ( newPrefsInitialized ) {
gPrefs - > DeleteEntry ( wxT ( " /NewPrefsInitialized " ) , true ) ; // take group as well if empty
}
// record the Prefs version for future checking (this has not been used for a very
// long time).
gPrefs - > Write ( wxT ( " /PrefsVersion " ) , wxString ( wxT ( AUDACITY_PREFS_VERSION_STRING ) ) ) ;
// Check if some prefs updates need to happen based on audacity version.
// Unfortunately we can't use the PrefsVersion prefs key because that resets things.
// In the future we may want to integrate that better.
// these are done on a case-by-case basis for now so they must be backwards compatible
// (meaning the changes won't mess audacity up if the user goes back to an earlier version)
int vMajor = gPrefs - > Read ( wxT ( " /Version/Major " ) , ( long ) 0 ) ;
int vMinor = gPrefs - > Read ( wxT ( " /Version/Minor " ) , ( long ) 0 ) ;
int vMicro = gPrefs - > Read ( wxT ( " /Version/Micro " ) , ( long ) 0 ) ;
gPrefs - > SetVersionKeysInit ( vMajor , vMinor , vMicro ) ; // make a note of these initial values
// for use by ToolManager::ReadConfig()
// These integer version keys were introduced april 4 2011 for 1.3.13
// The device toolbar needs to be enabled due to removal of source selection features in
// the mixer toolbar.
if ( ( vMajor < 1 ) | |
( vMajor = = 1 & & vMinor < 3 ) | |
( vMajor = = 1 & & vMinor = = 3 & & vMicro < 13 ) ) {
// Do a full reset of the Device Toolbar to get it on the screen.
if ( gPrefs - > Exists ( wxT ( " /GUI/ToolBars/Device " ) ) )
gPrefs - > DeleteGroup ( wxT ( " /GUI/ToolBars/Device " ) ) ;
// We keep the mixer toolbar prefs (shown/not shown)
// the width of the mixer toolbar may have shrunk, the prefs will keep the larger value
// if the user had a device that had more than one source.
if ( gPrefs - > Exists ( wxT ( " /GUI/ToolBars/Mixer " ) ) ) {
// Use the default width
gPrefs - > Write ( wxT ( " /GUI/ToolBars/Mixer/W " ) , - 1 ) ;
}
}
// In 2.1.0, the Meter toolbar was split and lengthened, but strange arrangements happen
// if upgrading due to the extra length. So, if a user is upgrading, use the pre-2.1.0
// lengths, but still use the NEW split versions.
if ( gPrefs - > Exists ( wxT ( " /GUI/ToolBars/Meter " ) ) & &
! gPrefs - > Exists ( wxT ( " /GUI/ToolBars/CombinedMeter " ) ) ) {
// Read in all of the existing values
long dock , order , show , x , y , w , h ;
gPrefs - > Read ( wxT ( " /GUI/ToolBars/Meter/Dock " ) , & dock , - 1 ) ;
gPrefs - > Read ( wxT ( " /GUI/ToolBars/Meter/Order " ) , & order , - 1 ) ;
gPrefs - > Read ( wxT ( " /GUI/ToolBars/Meter/Show " ) , & show , - 1 ) ;
gPrefs - > Read ( wxT ( " /GUI/ToolBars/Meter/X " ) , & x , - 1 ) ;
gPrefs - > Read ( wxT ( " /GUI/ToolBars/Meter/Y " ) , & y , - 1 ) ;
gPrefs - > Read ( wxT ( " /GUI/ToolBars/Meter/W " ) , & w , - 1 ) ;
gPrefs - > Read ( wxT ( " /GUI/ToolBars/Meter/H " ) , & h , - 1 ) ;
// "Order" must be adjusted since we're inserting two NEW toolbars
if ( dock > 0 ) {
wxString oldPath = gPrefs - > GetPath ( ) ;
gPrefs - > SetPath ( wxT ( " /GUI/ToolBars " ) ) ;
wxString bar ;
long ndx = 0 ;
bool cont = gPrefs - > GetFirstGroup ( bar , ndx ) ;
while ( cont ) {
long o ;
if ( gPrefs - > Read ( bar + wxT ( " /Order " ) , & o ) & & o > = order ) {
gPrefs - > Write ( bar + wxT ( " /Order " ) , o + 2 ) ;
}
cont = gPrefs - > GetNextGroup ( bar , ndx ) ;
}
gPrefs - > SetPath ( oldPath ) ;
// And override the height
h = 27 ;
}
// Write the split meter bar values
gPrefs - > Write ( wxT ( " /GUI/ToolBars/RecordMeter/Dock " ) , dock ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/RecordMeter/Order " ) , order ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/RecordMeter/Show " ) , show ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/RecordMeter/X " ) , - 1 ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/RecordMeter/Y " ) , - 1 ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/RecordMeter/W " ) , w ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/RecordMeter/H " ) , h ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/PlayMeter/Dock " ) , dock ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/PlayMeter/Order " ) , order + 1 ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/PlayMeter/Show " ) , show ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/PlayMeter/X " ) , - 1 ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/PlayMeter/Y " ) , - 1 ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/PlayMeter/W " ) , w ) ;
gPrefs - > Write ( wxT ( " /GUI/ToolBars/PlayMeter/H " ) , h ) ;
// And hide the old combined meter bar
gPrefs - > Write ( wxT ( " /GUI/ToolBars/Meter/Dock " ) , - 1 ) ;
}
// Upgrading pre 2.2.0 configs we assume extended set of defaults.
if ( ( 0 < vMajor & & vMajor < 2 ) | |
( vMajor = = 2 & & vMinor < 2 ) )
{
gPrefs - > Write ( wxT ( " /GUI/Shortcuts/FullDefaults " ) , 1 ) ;
}
// write out the version numbers to the prefs file for future checking
gPrefs - > Write ( wxT ( " /Version/Major " ) , AUDACITY_VERSION ) ;
gPrefs - > Write ( wxT ( " /Version/Minor " ) , AUDACITY_RELEASE ) ;
gPrefs - > Write ( wxT ( " /Version/Micro " ) , AUDACITY_REVISION ) ;
gPrefs - > Flush ( ) ;
}
}
2015-07-15 04:33:53 +00:00
static bool gInited = false ;
2019-04-26 03:59:42 +00:00
static bool gIsQuitting = false ;
2015-07-15 04:33:53 +00:00
2019-04-26 04:43:17 +00:00
static void QuitAudacity ( bool bForce )
2015-07-15 04:33:53 +00:00
{
2019-04-26 03:59:42 +00:00
// guard against recursion
2015-07-15 04:33:53 +00:00
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-05-29 18:19:01 +00:00
//- if (!AllProjects{}.empty())
2015-07-15 04:33:53 +00:00
/*start+*/
2019-05-29 18:19:01 +00:00
if ( AllProjects { } . empty ( ) )
2015-07-15 04:33:53 +00:00
{
# ifdef __WXMAC__
2019-04-25 20:49:31 +00:00
Clipboard : : Get ( ) . Clear ( ) ;
2015-07-15 04:33:53 +00:00
# endif
}
else
/*end+*/
{
2019-05-29 19:02:44 +00:00
if ( AllProjects { } . size ( ) )
// PRL: Always did at least once before close might be vetoed
// though I don't know why that is important
ProjectManager : : SaveWindowSize ( ) ;
2019-04-26 03:59:42 +00:00
bool closedAll = AllProjects : : Close ( bForce ) ;
if ( ! closedAll )
2015-07-15 04:33:53 +00:00
{
2019-04-26 03:59:42 +00:00
gIsQuitting = false ;
return ;
2015-07-15 04:33:53 +00:00
}
}
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
2019-03-30 19:19:26 +00:00
//temporarily 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 ( ) ;
}
}
2019-04-26 04:43:17 +00:00
static void QuitAudacity ( )
2015-07-15 04:33:53 +00:00
{
QuitAudacity ( false ) ;
}
# 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 ;
}
2019-05-29 18:19:01 +00:00
if ( AllProjects { } . 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.
2019-04-29 06:22:08 +00:00
if ( AllProjects { } . empty ( ) )
( void ) ProjectManager : : New ( ) ;
2015-07-15 04:33:53 +00:00
}
# endif //__WXMAC__
// 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 )
# endif
2019-04-26 04:43:17 +00:00
// Associate the handler with the menu id on all operating systems, even
// if they don't have an application menu bar like in macOS, so that
// other parts of the program can send the application a shut-down
// event
EVT_MENU ( wxID_EXIT , AudacityApp : : OnMenuExit )
2015-07-15 04:33:53 +00:00
# ifndef __WXMSW__
EVT_SOCKET ( ID_IPC_SERVER , AudacityApp : : OnServerEvent )
EVT_SOCKET ( ID_IPC_SOCKET , AudacityApp : : OnSocketEvent )
# endif
// Recent file event handlers.
2019-04-24 00:57:18 +00:00
EVT_MENU ( FileHistory : : ID_RECENT_CLEAR , AudacityApp : : OnMRUClear )
EVT_MENU_RANGE ( FileHistory : : ID_RECENT_FIRST , FileHistory : : ID_RECENT_LAST ,
AudacityApp : : OnMRUFile )
2015-07-15 04:33:53 +00:00
// 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.
2019-03-07 19:50:22 +00:00
bool AudacityApp : : MRUOpen ( const FilePath & fullPathStr ) {
2019-06-08 20:05:22 +00:00
// Most of the checks below are copied from ProjectManager::OpenFiles.
2015-07-15 04:33:53 +00:00
// - 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.
2019-06-08 20:05:22 +00:00
if ( ProjectFileManager : : IsAlreadyOpen ( fullPathStr ) )
2015-07-15 04:33:53 +00:00
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.
2019-04-29 06:22:08 +00:00
if ( proj & & (
2019-06-06 13:55:34 +00:00
ProjectHistory : : Get ( * proj ) . GetDirty ( ) | |
2019-04-29 06:22:08 +00:00
! TrackList : : Get ( * proj ) . empty ( )
) )
2016-12-20 18:13:00 +00:00
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.
2019-04-29 06:22:08 +00:00
( void ) ProjectManager : : OpenProject ( proj , fullPathStr ) ;
2015-07-15 04:33:53 +00:00
}
else {
// File doesn't exist - remove file from history
2019-12-07 19:30:07 +00:00
AudacityMessageBox (
XO (
" %s could not be found. \n \n It has been removed from the list of recent files. " )
. Format ( 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 ) )
{
2019-04-24 00:57:18 +00:00
FileHistory : : Global ( ) . Clear ( ) ;
2015-07-15 04:33:53 +00:00
}
//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 ) {
2019-04-24 00:57:18 +00:00
int n = event . GetId ( ) - FileHistory : : ID_RECENT_FIRST ;
auto & history = FileHistory : : Global ( ) ;
const auto & fullPathStr = history . 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.
2019-06-08 20:05:22 +00:00
if ( ! ProjectFileManager : : IsAlreadyOpen ( fullPathStr ) & & ! MRUOpen ( fullPathStr ) )
2019-04-24 00:57:18 +00:00
history . RemoveFileFromHistory ( n ) ;
2015-07-15 04:33:53 +00:00
}
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 ) {
2019-05-28 17:12:56 +00:00
auto & window = GetProjectFrame ( * project ) ;
window . Maximize ( ) ;
window . Raise ( ) ;
window . RequestUserAttention ( ) ;
2015-07-15 04:33:53 +00:00
}
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
2019-04-25 20:34:07 +00:00
if ( MissingAliasFilesDialog : : ShouldShow ( ) ) {
2015-07-15 04:33:53 +00:00
// find which project owns the blockfile
// note: there may be more than 1, but just go with the first one.
2019-05-29 18:19:01 +00:00
//size_t numProjects = AllProjects{}.size();
2019-04-25 20:34:07 +00:00
auto marked = MissingAliasFilesDialog : : Marked ( ) ;
2019-05-29 18:19:01 +00:00
auto offendingProject = marked . second ;
2019-04-25 20:34:07 +00:00
wxString missingFileName = marked . first ;
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 ) {
2019-05-28 17:12:56 +00:00
auto & window = GetProjectFrame ( * offendingProject ) ;
window . Iconize ( false ) ;
window . Raise ( ) ;
2015-07-15 04:33:53 +00:00
2019-12-05 18:38:07 +00:00
auto errorMessage = XO (
2015-07-15 04:33:53 +00:00
" 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 \
2019-12-05 18:38:07 +00:00
locations of the missing files . " ).Format( missingFileName ) ;
2015-07-15 04:33:53 +00:00
// if an old dialog exists, raise it if it is
2019-04-30 11:18:56 +00:00
if ( auto dialog = MissingAliasFilesDialog : : Find ( * offendingProject ) )
dialog - > Raise ( ) ;
else {
2019-12-08 05:25:47 +00:00
MissingAliasFilesDialog : : Show ( offendingProject . get ( ) , XO ( " Files Missing " ) ,
2015-07-15 04:33:53 +00:00
errorMessage , wxT ( " " ) , true ) ;
}
}
// Only show this warning once per event (playback/menu item/etc).
2019-04-25 20:34:07 +00:00
MissingAliasFilesDialog : : SetShouldShow ( false ) ;
2015-07-15 04:33:53 +00:00
}
}
# 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
void AudacityApp : : OnFatalException ( )
{
# if defined(EXPERIMENTAL_CRASH_REPORT)
2019-05-17 20:29:31 +00:00
CrashReport : : Generate ( wxDebugReport : : Context_Exception ) ;
2015-07-15 04:33:53 +00:00
# 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 ) {
2019-06-06 13:55:34 +00:00
ProjectHistory : : Get ( * pProject ) . RollbackState ( ) ;
2016-11-08 15:10:50 +00:00
2019-01-28 17:50:20 +00:00
// Forget pending changes in the TrackList
2019-05-06 23:00:10 +00:00
TrackList : : Get ( * pProject ) . ClearPendingTracks ( ) ;
2017-08-24 18:31:51 +00:00
2019-05-28 17:12:56 +00:00
ProjectWindow : : Get ( * pProject ) . RedrawProject ( ) ;
2019-01-28 17:50:20 +00:00
}
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
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 ;
2019-04-26 14:20:49 +00:00
// cause initialization of wxWidgets' global logger target
( void ) AudacityLogger : : Get ( ) ;
2015-07-15 04:33:53 +00:00
# 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
//
2019-04-25 15:23:18 +00:00
FilePaths audacityPathList ;
2015-07-15 04:33:53 +00:00
# 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 . */
2019-04-25 15:04:21 +00:00
FileNames : : SetDefaultTempDir ( wxString : : Format (
wxT ( " %s/audacity-%s " ) , envTempDir , wxGetUserId ( ) ) ) ;
2017-11-07 16:07:04 +00:00
} else {
/* On Unix systems, the default temp dir is in /var/tmp. */
2019-04-25 15:04:21 +00:00
FileNames : : SetDefaultTempDir ( wxString : : Format (
wxT ( " /var/tmp/audacity-%s " ) , wxGetUserId ( ) ) ) ;
2017-11-07 16:07:04 +00:00
}
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 ( ) )
2019-04-25 15:23:18 +00:00
FileNames : : AddMultiPathsToPathList ( pathVar , audacityPathList ) ;
FileNames : : AddUniquePathToPathList ( : : wxGetCwd ( ) , audacityPathList ) ;
2015-07-15 04:33:53 +00:00
# ifdef AUDACITY_NAME
2019-04-25 15:23:18 +00:00
FileNames : : 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 ) ;
2019-04-25 15:23:18 +00:00
FileNames : : AddUniquePathToPathList ( wxString : : Format ( wxT ( " %s/share/%s " ) ,
2016-02-01 01:39:24 +00:00
wxT ( INSTALL_PREFIX ) , wxT ( AUDACITY_NAME ) ) ,
audacityPathList ) ;
2019-04-25 15:23:18 +00:00
FileNames : : 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
2019-04-25 15:23:18 +00:00
FileNames : : 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 ) ;
2019-04-25 15:23:18 +00:00
FileNames : : AddUniquePathToPathList ( wxString : : Format ( wxT ( " %s/share/audacity " ) ,
2016-02-01 01:39:24 +00:00
wxT ( INSTALL_PREFIX ) ) ,
audacityPathList ) ;
2019-04-25 15:23:18 +00:00
FileNames : : 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
2019-04-25 15:23:18 +00:00
FileNames : : 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
2019-04-25 15:23:18 +00:00
FileNames : : 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 ] ) ;
2019-04-25 15:23:18 +00:00
FileNames : : AddUniquePathToPathList ( progPath , audacityPathList ) ;
FileNames : : 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 ( ) ) ;
2019-04-25 15:04:21 +00:00
FileNames : : SetDefaultTempDir ( wxString : : Format (
wxT ( " %s \\ SessionData " ) , 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 ] ) ;
2019-04-25 15:23:18 +00:00
FileNames : : AddUniquePathToPathList ( progPath , audacityPathList ) ;
2015-07-15 04:33:53 +00:00
// If Audacity is a "bundle" package, then the root directory is
// the great-great-grandparent of the directory containing the executable.
2019-04-25 15:23:18 +00:00
//FileNames::AddUniquePathToPathList(progPath + wxT("/../../../"), audacityPathList);
2015-07-15 04:33:53 +00:00
// These allow for searching the "bundle"
2019-04-25 15:23:18 +00:00
FileNames : : AddUniquePathToPathList (
progPath + wxT ( " /../ " ) , audacityPathList ) ;
FileNames : : 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.
2019-04-25 15:04:21 +00:00
FileNames : : SetDefaultTempDir ( wxString : : Format (
wxT ( " %s/Library/Application Support/audacity/SessionData " ) , tmpDirLoc ) ) ;
2016-07-06 11:10:26 +00:00
2019-04-25 15:04:21 +00:00
//FileNames::SetDefaultTempDir( wxString::Format(
// wxT("%s/audacity-%s"),
2017-10-09 05:03:14 +00:00
// tmpDirLoc,
2019-04-25 15:04:21 +00:00
// wxGetUserId() ) );
2015-07-15 04:33:53 +00:00
# endif //__WXMAC__
2019-04-25 15:23:18 +00:00
FileNames : : SetAudacityPathList ( std : : move ( audacityPathList ) ) ;
2015-07-15 04:33:53 +00:00
// 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
2019-07-09 16:14:42 +00:00
wxFileName configFileName ( FileNames : : DataDir ( ) , wxT ( " audacity.cfg " ) ) ;
InitPreferences ( configFileName ) ;
2019-05-11 11:36:02 +00:00
PopulatePreferences ( ) ;
2019-06-28 12:12:16 +00:00
// This test must follow PopulatePreferences, because if an error message
// must be shown, we need internationalization to have been initialized
// first, which was done in PopulatePreferences
if ( ! CheckWritablePreferences ( ) ) {
2019-07-09 16:14:42 +00:00
: : AudacityMessageBox (
UnwritablePreferencesErrorMessage ( configFileName ) ) ;
2019-06-28 12:12:16 +00:00
return false ;
}
2015-07-15 04:33:53 +00:00
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
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
{
2019-12-07 19:30:07 +00:00
wxPrintf ( AutoSaveFile : : FailureMessage ( fileName ) . Translation ( ) ) ; //Debug()?
2015-07-15 04:33:53 +00:00
}
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 ) ;
2019-10-03 19:24:06 +00:00
temporarywindow . Raise ( ) ;
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 ( ) ;
2019-06-10 19:42:38 +00:00
AudioIO : : Init ( ) ;
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
2019-04-24 00:57:18 +00:00
auto & recentFiles = FileHistory : : Global ( ) ;
recentFiles . UseMenu ( recentMenu ) ;
recentFiles . 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.
{
2019-04-29 06:22:08 +00:00
project = ProjectManager : : New ( ) ;
2016-02-01 01:39:24 +00:00
wxWindow * pWnd = MakeHijackPanel ( ) ;
if ( pWnd )
{
2019-05-28 17:12:56 +00:00
auto & window = GetProjectFrame ( * project ) ;
window . Show ( false ) ;
pWnd - > SetParent ( & window ) ;
2016-02-01 01:39:24 +00:00
SetTopWindow ( pWnd ) ;
pWnd - > Show ( true ) ;
}
}
2015-07-15 04:33:53 +00:00
2019-05-27 14:17:16 +00:00
if ( ProjectSettings : : Get ( * project ) . GetShowSplashScreen ( ) ) {
2017-02-25 22:30:23 +00:00
// 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();
2019-06-13 03:51:23 +00:00
SplashDialog : : DoHelpWelcome ( * project ) ;
2017-02-25 22:30:23 +00:00
}
2015-07-15 04:33:53 +00:00
# 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 " ) ) )
{
2020-01-07 21:00:36 +00:00
RunBenchmark ( nullptr , ProjectSettings : : Get ( * project ) ) ;
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 ) ;
mTimer . SetOwner ( this , kAudacityAppTimerID ) ;
mTimer . Start ( 200 ) ;
2015-07-28 20:23:30 +00:00
2019-06-10 23:10:07 +00:00
# ifdef EXPERIMENTAL_EASY_CHANGE_KEY_BINDINGS
CommandManager : : SetMenuHook ( [ ] ( const CommandID & id ) {
if ( : : wxGetMouseState ( ) . ShiftDown ( ) ) {
// Only want one page of the preferences
PrefsDialog : : Factories factories ;
factories . push_back ( KeyConfigPrefsFactory ( id ) ) ;
2020-01-04 17:24:14 +00:00
const auto pProject = GetActiveProject ( ) ;
auto pWindow = FindProjectFrame ( pProject ) ;
GlobalPrefsDialog dialog ( pWindow , pProject , factories ) ;
2019-06-10 23:10:07 +00:00
dialog . ShowModal ( ) ;
MenuCreator : : RebuildAllMenuBars ( ) ;
return true ;
}
else
return false ;
} ) ;
# endif
2019-12-15 22:10:51 +00:00
# if defined(__WXMAC__)
// The first time this version of Audacity is run or when the preferences
// are reset, execute the "tccutil" command to reset the microphone permissions
// currently assigned to Audacity. The end result is that the user will be
// prompted to approve/deny Audacity access (again).
//
// This should resolve confusion of why Audacity appears to record, but only
// gets silence due to Audacity being denied microphone access previously.
bool permsReset = false ;
gPrefs - > Read ( wxT ( " /MicrophonePermissionsReset " ) , & permsReset , false ) ;
if ( ! permsReset ) {
system ( " tccutil reset Microphone org.audacityteam.audacity " ) ;
gPrefs - > Write ( wxT ( " /MicrophonePermissionsReset " ) , true ) ;
}
# endif
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 ( ) ;
2019-05-22 00:43:49 +00:00
if ( project ) {
auto token = ProjectAudioIO : : Get ( * project ) . GetAudioIOToken ( ) ;
auto & scrubber = Scrubber : : Get ( * project ) ;
auto scrubbing = scrubber . HasMark ( ) ;
if ( scrubbing )
scrubber . Cancel ( ) ;
auto gAudioIO = AudioIO : : Get ( ) ;
if ( ( token > 0 & &
gAudioIO - > IsAudioTokenActive ( token ) & &
gAudioIO - > GetNumCaptureChannels ( ) = = 0 ) | |
scrubbing )
// ESC out of other play (but not record)
ProjectAudioManager : : Get ( * project ) . Stop ( ) ;
else
event . Skip ( ) ;
}
2016-04-20 19:34:49 +00:00
}
2019-05-22 00:43:49 +00:00
event . Skip ( ) ;
2016-04-20 19:34:49 +00:00
}
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 ( " " ) ) ;
2019-04-25 15:04:21 +00:00
auto tempDefaultLoc = FileNames : : DefaultTempDir ( ) ;
2015-07-15 04:33:53 +00:00
2019-03-15 18:41:21 +00:00
wxString temp ;
2015-07-15 04:33:53 +00:00
# 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
2019-04-25 15:04:21 +00:00
if ( FileNames : : IsTempDirectoryNameOK ( tempFromPrefs ) )
2017-01-03 15:35:22 +00:00
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
2019-04-25 15:04:21 +00:00
if ( ! FileNames : : IsTempDirectoryNameOK ( tempFromPrefs ) ) {
2019-12-07 19:30:07 +00:00
AudacityMessageBox ( XO (
" 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 {
2019-12-07 19:30:07 +00:00
AudacityMessageBox ( XO (
" 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
PrefsDialog : : Factories factories ;
2019-01-14 01:55:52 +00:00
factories . push_back ( DirectoriesPrefsFactory ( ) ) ;
2020-01-04 17:24:14 +00:00
GlobalPrefsDialog dialog ( nullptr , nullptr , factories ) ;
2015-07-15 04:33:53 +00:00
dialog . ShowModal ( ) ;
2019-12-07 19:30:07 +00:00
AudacityMessageBox ( XO (
" 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
2019-12-07 19:30:07 +00:00
auto runningTwoCopiesStr = XO ( " Running two copies of Audacity simultaneously may cause \n data loss or cause your system to crash. \n \n " ) ;
2015-07-15 04:33:53 +00:00
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.
2019-12-07 19:30:07 +00:00
auto prompt = XO (
" Audacity was not able to lock the temporary files directory. \n This folder may be in use by another copy of Audacity. \n " )
+ runningTwoCopiesStr
+ XO ( " Do you still want to start Audacity? " ) ;
int action = AudacityMessageBox (
prompt ,
XO ( " 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.
2019-03-07 19:50:22 +00:00
FilePaths filenames ;
2018-11-04 17:03:27 +00:00
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.
2019-12-07 19:30:07 +00:00
auto prompt = XO (
" The system has detected that another copy of Audacity is running. \n " )
+ runningTwoCopiesStr
+ XO (
" Use the New or Open commands in the currently running Audacity \n process to open multiple projects simultaneously. \n " ) ;
AudacityMessageBox (
prompt , XO ( " Audacity is already running " ) ,
wxOK | wxICON_ERROR ) ;
2019-10-03 16:17:41 +00:00
# ifdef __WXMAC__
// Bug 2052
// On mac, the lock file may persist and stop Audacity starting properly.
2019-10-04 10:31:23 +00:00
auto lockFileName = wxFileName ( dir , name ) ;
bool bIsLocked = lockFileName . IsOk ( ) & & lockFileName . FileExists ( ) ;
if ( bIsLocked ) {
2019-12-07 19:30:07 +00:00
int action = AudacityMessageBox (
XO ( " If you're sure another copy of Audacity isn't \n running, Audacity can skip the test for \n 'Audacity already running' next time \n by removing the lock file: \n \n %s \n \n Do you want to do that? " )
. Format ( lockFileName . GetFullName ( ) ) ,
XO ( " Possible Lock File Problem " ) ,
2019-10-04 10:31:23 +00:00
wxYES_NO | wxICON_EXCLAMATION ,
NULL ) ;
if ( action = = wxYES ) {
// If locked, unlock.
lockFileName . SetPermissions ( wxS_DEFAULT ) ;
: : wxRemoveFile ( lockFileName . GetFullName ( ) ) ;
}
2019-10-03 16:17:41 +00:00
}
# endif
2015-07-15 04:33:53 +00:00
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
}
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.
2019-04-26 03:59:42 +00:00
gIsQuitting = true ;
2019-05-29 19:02:44 +00:00
if ( AllProjects { } . size ( ) )
// PRL: Always did at least once before close might be vetoed
// though I don't know why that is important
ProjectManager : : SaveWindowSize ( ) ;
2019-04-26 03:59:42 +00:00
bool closedAll = AllProjects : : Close ( force ) ;
if ( ! closedAll )
{
gIsQuitting = false ;
event . Veto ( ) ;
2015-07-15 04:33:53 +00:00
}
}
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 ( ) ;
}
}
2019-04-24 00:57:18 +00:00
FileHistory : : Global ( ) . Save ( * gPrefs , wxT ( " RecentFiles " ) ) ;
2015-07-15 04:33:53 +00:00
FinishPreferences ( ) ;
# ifdef USE_FFMPEG
DropFFmpegLibs ( ) ;
# endif
DeinitFFT ( ) ;
2019-06-10 19:42:38 +00:00
AudioIO : : Deinit ( ) ;
2016-08-16 01:19:50 +00:00
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.
2019-05-29 18:19:01 +00:00
if ( AllProjects { } . empty ( ) )
2019-04-29 06:22:08 +00:00
( void ) ProjectManager : : New ( ) ;
2015-07-15 04:33:53 +00:00
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.
2019-05-29 18:19:01 +00:00
if ( AllProjects { } . empty ( ) )
2019-04-29 06:22:08 +00:00
ProjectManager : : OpenFiles ( NULL ) ;
2015-07-15 04:33:53 +00:00
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.
2019-05-29 18:19:01 +00:00
if ( AllProjects { } . empty ( ) ) {
2020-01-04 17:24:14 +00:00
GlobalPrefsDialog dialog ( nullptr /* parent */ , nullptr ) ;
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.
2019-05-29 18:19:01 +00:00
// if(AllProjects{}.empty())
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.
2019-05-29 18:19:01 +00:00
event . Skip ( AllProjects { } . empty ( ) ) ;
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 (
2019-12-07 19:30:07 +00:00
XO (
" Audacity project (.AUP) files are not currently \n associated with Audacity. \n \n Associate them, so they open on double-click? " ) ,
XO ( " Audacity Project Files " ) ,
2015-07-15 04:33:53 +00:00
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