/**********************************************************************

  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.

*//*******************************************************************/

#include "Audacity.h" // This should always be included first; for USE_* macros and __UNIX__
#include "AudacityApp.h"

#include "Experimental.h"

#if 0
// This may be used to debug memory leaks.
// See: Visual Leak Dectector @ http://vld.codeplex.com/
#include <vld.h>
#endif

#include <wx/setup.h> // for wxUSE_* macros
#include <wx/wxcrtvararg.h>
#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

#if defined(__WXMSW__)
#include <wx/msw/registry.h> // for wxRegKey
#endif

#include "AudacityLogger.h"
#include "AboutDialog.h"
#include "AColor.h"
#include "AudioIO.h"
#include "Benchmark.h"
#include "Clipboard.h"
#include "CrashReport.h"
#include "DirManager.h"
#include "commands/CommandHandler.h"
#include "commands/AppCommandEvent.h"
#include "widgets/ASlider.h"
#include "FFmpeg.h"
//#include "LangChoice.h"
#include "Languages.h"
#include "Menus.h"
#include "MissingAliasFileDialog.h"
#include "PluginManager.h"
#include "Project.h"
#include "ProjectAudioIO.h"
#include "ProjectAudioManager.h"
#include "ProjectFileManager.h"
#include "ProjectHistory.h"
#include "ProjectManager.h"
#include "ProjectSettings.h"
#include "ProjectWindow.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 "AutoRecoveryDialog.h"
#include "SplashDialog.h"
#include "FFT.h"
#include "BlockFile.h"
#include "ondemand/ODManager.h"
#include "widgets/AudacityMessageBox.h"
#include "prefs/DirectoriesPrefs.h"
#include "prefs/GUIPrefs.h"
#include "tracks/ui/Scrubbing.h"
#include "widgets/FileHistory.h"

#ifdef EXPERIMENTAL_EASY_CHANGE_KEY_BINDINGS
#include "prefs/KeyConfigPrefs.h"
#endif

//temporarily commented out till it is added to all projects
//#include "Profiler.h"

#include "ModuleManager.h"

#include "import/Import.h"

#if defined(EXPERIMENTAL_CRASH_REPORT)
#include <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__)
//#if wxCHECK_VERSION(3, 0, 2) && !wxCHECK_VERSION(3, 1, 0)
#include <wx/init.h>
//#endif
// These lines ensure that Audacity gets WindowsXP themes.
// Without them we get the old-style Windows98/2000 look under XP.
#  if !defined(__WXWINCE__)
#     pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#  endif

// These lines allows conditional inclusion of the various libraries
// that Audacity can use.

#  if defined(USE_LIBFLAC)
#     pragma comment(lib, "libflac++")
#     pragma comment(lib, "libflac")
#  endif

#  if defined(USE_LIBID3TAG)
#     pragma comment(lib, "libid3tag")
#  endif

#  if defined(USE_LIBMAD)
#     pragma comment(lib, "libmad")
#  endif

#  if defined(USE_LIBTWOLAME)
#     pragma comment(lib, "twolame")
#  endif

#  if defined(USE_LIBVORBIS)
#     pragma comment(lib, "libogg")
#     pragma comment(lib, "libvorbis")
#  endif

#  if defined(USE_LV2)
#     pragma comment(lib, "lv2")
#  endif

#  if defined(USE_MIDI)
#     pragma comment(lib, "portsmf")
#     endif

#  if defined(EXPERIMENTAL_MIDI_OUT)
#     pragma comment(lib, "portmidi")
#  endif

#  if defined(EXPERIMENTAL_SCOREALIGN)
#     pragma comment(lib, "libscorealign")
#  endif

#  if defined(USE_NYQUIST)
#     pragma comment(lib, "libnyquist")
#  endif

#  if defined(USE_PORTMIXER)
#     pragma comment(lib, "portmixer")
#  endif

#  if defined(USE_SBSMS)
#     pragma comment(lib, "sbsms")
#  endif

#  if defined(USE_SOUNDTOUCH)
#     pragma comment(lib, "soundtouch")
#  endif

#  if defined(USE_VAMP)
#     pragma comment(lib, "libvamp")
#  endif

#  if defined(_DEBUG)
#     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
#  pragma comment(lib, "wxbase" V "u" D)
#  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")
#  pragma comment(lib, "wxpng"        D)
#  pragma comment(lib, "wxzlib"       D)
#  pragma comment(lib, "wxjpeg"       D)
#  pragma comment(lib, "wxtiff"       D)

#  undef V
#  undef D

#endif //(__WXMSW__)

// DA: Logo for Splash Screen
#ifdef EXPERIMENTAL_DA
#include "../images/DarkAudacityLogoWithName.xpm"
#else
#include "../images/AudacityLogoWithName.xpm"
#endif


////////////////////////////////////////////////////////////
/// Custom events
////////////////////////////////////////////////////////////

#if 0
#ifdef __WXGTK__
static void wxOnAssert(const wxChar *fileName, int lineNumber, const wxChar *msg)
{
   if (msg)
      wxPrintf("ASSERTION FAILED: %s\n%s: %d\n", (const char *)wxString(msg).mb_str(), (const char *)wxString(fileName).mb_str(), lineNumber);
   else
      wxPrintf("ASSERTION FAILED!\n%s: %d\n", (const char *)wxString(fileName).mb_str(), lineNumber);

   // Force core dump
   int *i = 0;
   if (*i)
      exit(1);

   exit(0);
}
#endif
#endif

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)
      {
         AudacityMessageBox(
            XO("Failed to remove %s").Format(fullPath),
            XO("Failed!"));
      }
   }

   langCode = GUIPrefs::InitLang( langCode );

   // User requested that the preferences be completely reset
   if (resetPrefs)
   {
      // pop up a dialogue
      auto prompt = XO(
"Reset Preferences?\n\nThis 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);
      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);
   }

   // Upgrading pre 2.4.0 configs, the selection toolbar is now split.
   if ((0<vMajor && vMajor < 2) ||
       (vMajor == 2 && vMinor < 4))
   {
      gPrefs->Write(wxT("/GUI/Toolbars/Selection/W"),"");
      gPrefs->Write(wxT("/GUI/Toolbars/TimeToolBar/X"),-1);
      gPrefs->Write(wxT("/GUI/Toolbars/TimeToolBar/Y"),-1);
      gPrefs->Write(wxT("/GUI/Toolbars/TimeToolBar/H"),55);
      gPrefs->Write(wxT("/GUI/Toolbars/TimeToolBar/W"),251);
      gPrefs->Write(wxT("/GUI/Toolbars/TimeToolBar/DockV2"),2);
      gPrefs->Write(wxT("/GUI/Toolbars/TimeToolBar/Dock"),2);
      gPrefs->Write(wxT("/GUI/Toolbars/TimeToolBar/Path"),"0,0");
      gPrefs->Write(wxT("/GUI/Toolbars/TimeToolBar/Show"),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();
}

}

static bool gInited = false;
static bool gIsQuitting = false;

static void QuitAudacity(bool bForce)
{
   // guard against recursion
   if (gIsQuitting)
      return;

   gIsQuitting = true;

   wxTheApp->SetExitOnFrameDelete(true);

   // Try to close each open window.  If the user hits Cancel
   // in a Save Changes dialog, don't continue.
   // BG: unless force is true

   // BG: Are there any projects open?
   //-   if (!AllProjects{}.empty())
/*start+*/
   if (AllProjects{}.empty())
   {
#ifdef __WXMAC__
      Clipboard::Get().Clear();
#endif
   }
   else
/*end+*/
   {
      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();
      bool closedAll = AllProjects::Close( bForce );
      if ( !closedAll )
      {
         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
   //temporarily commented out till it is added to all projects
   //DELETE Profiler::Instance();

   //remove our logger
   std::unique_ptr<wxLog>{ wxLog::SetActiveTarget(NULL) }; // DELETE

   if (bForce)
   {
      wxExit();
   }
}

static void QuitAudacity()
{
   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;

static void interact_cb(GnomeClient * /* client */,
                        gint key,
                        GnomeDialogType /* type */,
                        gpointer /* data */)
{
   wxCloseEvent e(wxEVT_QUERY_END_SESSION, wxID_ANY);
   e.SetEventObject(&wxGetApp());
   e.SetCanVeto(true);

   wxGetApp().ProcessEvent(e);

   gnome_interaction_key_return(key, e.GetVeto());
}

static gboolean save_yourself_cb(GnomeClient *client,
                                 gint /* phase */,
                                 GnomeSaveStyle /* style */,
                                 gboolean shutdown,
                                 GnomeInteractStyle interact,
                                 gboolean /* fast */,
                                 gpointer /* user_data */)
{
   if (!shutdown || interact != GNOME_INTERACT_ANY) {
      return TRUE;
   }

   if (AllProjects{}.empty()) {
      return TRUE;
   }

   gnome_client_request_interaction(client,
                                    GNOME_DIALOG_NORMAL,
                                    interact_cb,
                                    NULL);

   return TRUE;
}

class GnomeShutdown
{
 public:
   GnomeShutdown()
   {
      mArgv[0].reset(strdup("Audacity"));

      mGnomeui = dlopen("libgnomeui-2.so.0", RTLD_NOW);
      if (!mGnomeui) {
         return;
      }

      mGnome = dlopen("libgnome-2.so.0", RTLD_NOW);
      if (!mGnome) {
         return;
      }

      _gnome_program_init_fn gnome_program_init = (_gnome_program_init_fn)
         dlsym(mGnome, "gnome_program_init");
      _libgnomeui_module_info_get_fn libgnomeui_module_info_get = (_libgnomeui_module_info_get_fn)
         dlsym(mGnomeui, "libgnomeui_module_info_get");
      _gnome_master_client_fn gnome_master_client = (_gnome_master_client_fn)
         dlsym(mGnomeui, "gnome_master_client");

      gnome_client_request_interaction = (_gnome_client_request_interaction_fn)
         dlsym(mGnomeui, "gnome_client_request_interaction");
      gnome_interaction_key_return = (_gnome_interaction_key_return_fn)
         dlsym(mGnomeui, "gnome_interaction_key_return");


      if (!gnome_program_init || !libgnomeui_module_info_get) {
         return;
      }

      gnome_program_init(mArgv[0].get(),
                         "1.0",
                         libgnomeui_module_info_get(),
                         1,
                         reinterpret_cast<char**>(mArgv),
                         NULL);

      mClient = gnome_master_client();
      if (mClient == NULL) {
         return;
      }

      g_signal_connect(mClient, "save-yourself", G_CALLBACK(save_yourself_cb), NULL);
   }

   virtual ~GnomeShutdown()
   {
      // Do not dlclose() the libraries here lest you want segfaults...
   }

 private:

   MallocString<> mArgv[1];
   void *mGnomeui;
   void *mGnome;
   GnomeClient *mClient;
};

// This variable exists to call the constructor and
// connect a signal for the 'save-yourself' message.
GnomeShutdown GnomeShutdownInstance;

#endif

// Where drag/drop or "Open With" filenames get stored until
// the timer routine gets around to picking them up.
static wxArrayString ofqueue;

//
// DDE support for opening multiple files with one instance
// of Audacity.
//

#define IPC_APPL wxT("audacity")
#define IPC_TOPIC wxT("System")

class IPCConn final : public wxConnection
{
public:
   IPCConn()
   : wxConnection()
   {
   };

   ~IPCConn()
   {
   };

   bool OnExec(const wxString & WXUNUSED(topic),
               const wxString & data)
   {
      // Add the filename to the queue.  It will be opened by
      // the OnTimer() event when it is safe to do so.
      ofqueue.push_back(data);

      return true;
   }
};

class IPCServ final : public wxServer
{
public:
   IPCServ(const wxString & appl)
   : wxServer()
   {
      Create(appl);
   };

   ~IPCServ()
   {
   };

   wxConnectionBase *OnAcceptConnection(const wxString & topic) override
   {
      if (topic != IPC_TOPIC) {
         return NULL;
      }

      // Trust wxWidgets framework to DELETE it
      return safenew IPCConn();
   };
};

#if defined(__WXMAC__)

IMPLEMENT_APP_NO_MAIN(AudacityApp)
IMPLEMENT_WX_THEME_SUPPORT

int main(int argc, char *argv[])
{
   wxDISABLE_DEBUG_SUPPORT();

   return wxEntry(argc, argv);
}

#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

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);
}

#else
IMPLEMENT_APP(AudacityApp)
#endif

#ifdef __WXMAC__

// in response of an open-document apple event
void AudacityApp::MacOpenFile(const wxString &fileName)
{
   ofqueue.push_back(fileName);
}

// in response of a print-document apple event
void AudacityApp::MacPrintFile(const wxString &fileName)
{
   ofqueue.push_back(fileName);
}

// in response of a open-application apple event
void AudacityApp::MacNewFile()
{
   if (!gInited)
      return;

   // This method should only be used on the Mac platform
   // when no project windows are open.

   if (AllProjects{}.empty())
      (void) ProjectManager::New();
}

#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)
   EVT_QUERY_END_SESSION(AudacityApp::OnQueryEndSession)
   EVT_END_SESSION(AudacityApp::OnEndSession)

   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

   // 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)

#ifndef __WXMSW__
   EVT_SOCKET(ID_IPC_SERVER, AudacityApp::OnServerEvent)
   EVT_SOCKET(ID_IPC_SOCKET, AudacityApp::OnSocketEvent)
#endif

   // Recent file event handlers.
   EVT_MENU(FileHistory::ID_RECENT_CLEAR, AudacityApp::OnMRUClear)
   EVT_MENU_RANGE(FileHistory::ID_RECENT_FIRST, FileHistory::ID_RECENT_LAST,
      AudacityApp::OnMRUFile)

   // Handle AppCommandEvents (usually from a script)
   EVT_APP_COMMAND(wxID_ANY, AudacityApp::OnReceiveCommand)

   // Global ESC key handling
   EVT_KEY_DOWN(AudacityApp::OnKeyDown)
END_EVENT_TABLE()

// backend for OnMRUFile
// TODO: Would be nice to make this handle not opening a file with more panache.
//  - Inform the user if DefaultOpenPath not set.
//  - Switch focus to correct instance of project window, if already open.
bool AudacityApp::MRUOpen(const FilePath &fullPathStr) {
   // Most of the checks below are copied from ProjectManager::OpenFiles.
   // - some rationalisation might be possible.

   AudacityProject *proj = GetActiveProject();

   if (!fullPathStr.empty())
   {
      // verify that the file exists
      if (wxFile::Exists(fullPathStr))
      {
         FileNames::UpdateDefaultPath(FileNames::Operation::Open, fullPathStr);

         // 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 (ProjectFileManager::IsAlreadyOpen(fullPathStr))
            return false;

         // DMM: If the project is dirty, that means it's been touched at
         // all, and it's not safe to open a NEW project directly in its
         // place.  Only if the project is brand-NEW clean and the user
         // hasn't done any action at all is it safe for Open to take place
         // inside the current project.
         //
         // If you try to Open a NEW project inside the current window when
         // there are no tracks, but there's an Undo history, etc, then
         // bad things can happen, including data files moving to the NEW
         // project directory, etc.
         if (proj && (
            ProjectHistory::Get( *proj ).GetDirty() ||
            !TrackList::Get( *proj ).empty()
         ) )
            proj = nullptr;
         // This project is clean; it's never been touched.  Therefore
         // all relevant member variables are in their initial state,
         // and it's okay to open a NEW project inside this window.
         ( void ) ProjectManager::OpenProject( proj, fullPathStr );
      }
      else {
         // File doesn't exist - remove file from history
         AudacityMessageBox(
            XO(
"%s could not be found.\n\nIt has been removed from the list of recent files.")
               .Format(fullPathStr) );
         return(false);
      }
   }
   return(true);
}

bool AudacityApp::SafeMRUOpen(const wxString &fullPathStr)
{
   return GuardedCall< bool >( [&]{ return MRUOpen( fullPathStr ); } );
}

void AudacityApp::OnMRUClear(wxCommandEvent& WXUNUSED(event))
{
   FileHistory::Global().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() - FileHistory::ID_RECENT_FIRST;
   auto &history = FileHistory::Global();
   const auto &fullPathStr = history.GetHistoryFile(n);

   // Try to open only if not already open.
   // Test IsAlreadyOpen() here even though AudacityProject::MRUOpen() also now checks,
   // because we don't want to RemoveFileFromHistory() just because it already exists,
   // and AudacityApp::OnMacOpenFile() calls MRUOpen() directly.
   // that method does not return the bad result.
   // 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.
   if (!ProjectFileManager::IsAlreadyOpen(fullPathStr) && !MRUOpen(fullPathStr))
      history.RemoveFileFromHistory(n);
}

void AudacityApp::OnTimer(wxTimerEvent& WXUNUSED(event))
{
   // Filenames are queued when Audacity receives a few of the
   // AppleEvent messages (via wxWidgets).  So, open any that are
   // in the queue and clean the queue.
   if (gInited) {
      if (ofqueue.size()) {
         // Load each file on the queue
         while (ofqueue.size()) {
            wxString name;
            name.swap(ofqueue[0]);
            ofqueue.erase( ofqueue.begin() );

            // Get the user's attention if no file name was specified
            if (name.empty()) {
               // Get the users attention
               AudacityProject *project = GetActiveProject();
               if (project) {
                  auto &window = GetProjectFrame( *project );
                  window.Maximize();
                  window.Raise();
                  window.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.
            // PRL: Catch any exceptions, don't try this file again, continue to
            // other files.
            if (!SafeMRUOpen(name)) {
               wxFAIL_MSG(wxT("MRUOpen failed"));
            }
         }
      }
   }

   // Check if a warning for missing aliased files should be displayed
   if (MissingAliasFilesDialog::ShouldShow()) {
      // find which project owns the blockfile
      // note: there may be more than 1, but just go with the first one.
      //size_t numProjects = AllProjects{}.size();
      auto marked = MissingAliasFilesDialog::Marked();
      auto offendingProject = marked.second;
      wxString missingFileName = marked.first;

      // if there are no projects open, don't show the warning (user has closed it)
      if (offendingProject) {
         auto &window = GetProjectFrame( *offendingProject );
         window.Iconize(false);
         window.Raise();

         auto errorMessage = XO(
"One or more external audio files could not be found.\n\
It is possible they were moved, deleted, or the drive they \
were on was unmounted.\n\
Silence is being substituted for the affected audio.\n\
The first detected missing file is:\n\
%s\n\
There may be additional missing files.\n\
Choose Help > Diagnostics > Check Dependencies to view a list of \
locations of the missing files.").Format( missingFileName ) ;

         // if an old dialog exists, raise it if it is
         if ( auto dialog = MissingAliasFilesDialog::Find( *offendingProject ) )
            dialog->Raise();
         else {
            MissingAliasFilesDialog::Show(offendingProject.get(), XO("Files Missing"),
                                   errorMessage, wxT(""), true);
         }
      }
      // Only show this warning once per event (playback/menu item/etc).
      MissingAliasFilesDialog::SetShouldShow(false);
   }
}

#if defined(__WXMSW__)
#define WL(lang, sublang) (lang), (sublang),
#else
#define WL(lang,sublang)
#endif

#if wxCHECK_VERSION(3, 0, 1)
wxLanguageInfo userLangs[] =
{
   // 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 },
};
#endif

void AudacityApp::OnFatalException()
{
#if defined(EXPERIMENTAL_CRASH_REPORT)
   CrashReport::Generate(wxDebugReport::Context_Exception);
#endif

   exit(-1);
}


#ifdef _MSC_VER
// If this is compiled with MSVC (Visual Studio)
#pragma warning( push )
#pragma warning( disable : 4702) // unreachable code warning.
#endif //_MSC_VER

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 ) {
      (void)e;// Compiler food
      // 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();
      auto pException = std::current_exception();
      CallAfter( [=]      // Capture pException by value!
      {

         // Restore the state of the project to what it was before the
         // failed operation
         if (pProject) {
            ProjectHistory::Get( *pProject ).RollbackState();

            // Forget pending changes in the TrackList
            TrackList::Get( *pProject ).ClearPendingTracks();

            ProjectWindow::Get( *pProject ).RedrawProject();
         }

         // Give the user an alert
         try { std::rethrow_exception( pException ); }
         catch( AudacityException &e )
            { e.DelayedHandlerAction(); }

      } );

      // 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;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif //_MSC_VER

int AudacityApp::FilterEvent(wxEvent & event)
{
   (void)event;// compiler food (stops unused parameter warning)
#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
   //
   if (event.GetEventType() == wxEVT_ACTIVATE)
   {
      wxActivateEvent & e = (wxActivateEvent &) event;

      if (e.GetEventObject() && e.GetActive() && e.GetEventObject()->IsKindOf(CLASSINFO(wxDialog)))
      {
         ((wxWindow *)e.GetEventObject())->SetFocus();
      }
   }
#endif


#if defined(__WXMAC__) || defined(__WXGTK__)
   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());
         window->SetFocus();
#if defined(__WXMAC__)
         window->NavigateIn();
#endif
      }
   }
#endif

   return Event_Skip;
}

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
}

AudacityApp::~AudacityApp()
{
}

// The `main program' equivalent, creating the windows and returning the
// main frame
bool AudacityApp::OnInit()
{
   // JKC: ANSWER-ME: Who actually added the event loop guarantor?
   // Although 'blame' says Leland, I think it came from a donated patch.

   // 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

   // Ensure we have an event loop during initialization
   wxEventLoopGuarantor eventLoop;

   // cause initialization of wxWidgets' global logger target
   (void) AudacityLogger::Get();

#if defined(__WXMAC__)
   // Disable window animation
   wxSystemOptions::SetOption(wxMAC_WINDOW_PLAIN_TRANSITION, 1);
#endif

   // Don't use AUDACITY_NAME here.
   // We want Audacity with a capital 'A'

// DA: App name
#ifndef EXPERIMENTAL_DA
   wxString appName = wxT("Audacity");
#else
   wxString appName = wxT("DarkAudacity");
#endif

   wxTheApp->SetAppName(appName);
   // Explicitly set since OSX will use it for the "Quit" menu item
   wxTheApp->SetAppDisplayName(appName);
   wxTheApp->SetVendorName(appName);

   ::wxInitAllImageHandlers();

   // AddHandler takes ownership
   wxFileSystem::AddHandler(safenew wxZipFSHandler);

   //
   // Paths: set search path and temp dir path
   //
   FilePaths audacityPathList;

#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();

   wxString envTempDir = wxGetenv(wxT("TMPDIR"));
   if (!envTempDir.empty()) {
      /* On Unix systems, the environment variable TMPDIR may point to
         an unusual path when /tmp and /var/tmp are not desirable. */
      FileNames::SetDefaultTempDir( wxString::Format(
         wxT("%s/audacity-%s"), envTempDir, wxGetUserId() ) );
   } else {
      /* On Unix systems, the default temp dir is in /var/tmp. */
      FileNames::SetDefaultTempDir( wxString::Format(
         wxT("/var/tmp/audacity-%s"), wxGetUserId() ) );
   }

// DA: Path env variable.
#ifndef EXPERIMENTAL_DA
   wxString pathVar = wxGetenv(wxT("AUDACITY_PATH"));
#else
   wxString pathVar = wxGetenv(wxT("DARKAUDACITY_PATH"));
#endif
   if (!pathVar.empty())
      FileNames::AddMultiPathsToPathList(pathVar, audacityPathList);
   FileNames::AddUniquePathToPathList(::wxGetCwd(), audacityPathList);

#ifdef AUDACITY_NAME
   FileNames::AddUniquePathToPathList(wxString::Format(wxT("%s/.%s-files"),
      home, wxT(AUDACITY_NAME)),
      audacityPathList);
   FileNames::AddUniquePathToPathList(wxString::Format(wxT("%s/share/%s"),
      wxT(INSTALL_PREFIX), wxT(AUDACITY_NAME)),
      audacityPathList);
   FileNames::AddUniquePathToPathList(wxString::Format(wxT("%s/share/doc/%s"),
      wxT(INSTALL_PREFIX), wxT(AUDACITY_NAME)),
      audacityPathList);
#else //AUDACITY_NAME
   FileNames::AddUniquePathToPathList(wxString::Format(wxT("%s/.audacity-files"),
      home),
      audacityPathList);
   FileNames::AddUniquePathToPathList(wxString::Format(wxT("%s/share/audacity"),
      wxT(INSTALL_PREFIX)),
      audacityPathList);
   FileNames::AddUniquePathToPathList(wxString::Format(wxT("%s/share/doc/audacity"),
      wxT(INSTALL_PREFIX)),
      audacityPathList);
#endif //AUDACITY_NAME

   FileNames::AddUniquePathToPathList(wxString::Format(wxT("%s/share/locale"),
      wxT(INSTALL_PREFIX)),
      audacityPathList);

   FileNames::AddUniquePathToPathList(wxString::Format(wxT("./locale")),
      audacityPathList);

#endif //__WXGTK__

// 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
   wxFileName tmpFile;
   tmpFile.AssignTempFileName(wxT("nn"));
   wxString tmpDirLoc = tmpFile.GetPath(wxPATH_GET_VOLUME);
   ::wxRemoveFile(tmpFile.GetFullPath());
#endif



   // 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]);
   FileNames::AddUniquePathToPathList(progPath, audacityPathList);
   FileNames::AddUniquePathToPathList(progPath + wxT("\\Languages"), audacityPathList);

   // See bug #1271 for explanation of location
   tmpDirLoc = FileNames::MkDir(wxStandardPaths::Get().GetUserLocalDataDir());
   FileNames::SetDefaultTempDir( wxString::Format(
      wxT("%s\\SessionData"), tmpDirLoc ) );
#endif //__WXWSW__

#ifdef __WXMAC__
   // On Mac OS X, the path to the Audacity program is in argv[0]
   wxString progPath = wxPathOnly(argv[0]);

   FileNames::AddUniquePathToPathList(progPath, audacityPathList);
   // If Audacity is a "bundle" package, then the root directory is
   // the great-great-grandparent of the directory containing the executable.
   //FileNames::AddUniquePathToPathList(progPath + wxT("/../../../"), audacityPathList);

   // These allow for searching the "bundle"
   FileNames::AddUniquePathToPathList(
      progPath + wxT("/../"), audacityPathList);
   FileNames::AddUniquePathToPathList(
      progPath + wxT("/../Resources"), audacityPathList);

   // JKC Bug 1220: Using an actual temp directory for session data on Mac was
   // wrong because it would get cleared out on a reboot.
   FileNames::SetDefaultTempDir( wxString::Format(
      wxT("%s/Library/Application Support/audacity/SessionData"), tmpDirLoc) );

   //FileNames::SetDefaultTempDir( wxString::Format(
   //   wxT("%s/audacity-%s"),
   //   tmpDirLoc,
   //   wxGetUserId() ) );
#endif //__WXMAC__

   FileNames::SetAudacityPathList( std::move( audacityPathList ) );

   // 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.
#if wxCHECK_VERSION(3, 0, 1)
   for (size_t i = 0, cnt = WXSIZEOF(userLangs); i < cnt; i++)
   {
      wxLocale::AddLanguage(userLangs[i]);
   }
#endif

   // Initialize preferences and language
   wxFileName configFileName(FileNames::DataDir(), wxT("audacity.cfg"));
   InitPreferences( configFileName );
   PopulatePreferences();
   // 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() ) {
      ::AudacityMessageBox(
         UnwritablePreferencesErrorMessage( configFileName ) );
      return false;
   }

#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
   this->AssociateFileTypes();
#endif

   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;
   }

   //<<<< 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.

   // 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.
   std::shared_ptr< wxCmdLineParser > parser{ ParseCommandLine().release() };
   if (!parser)
   {
      // Either user requested help or a parsing error occured
      exit(1);
   }

   if (parser->Found(wxT("v")))
   {
      wxPrintf("Audacity v%s\n", AUDACITY_VERSION_STRING);
      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( AutoSaveFile::FailureMessage( fileName ) .Translation() ); //Debug()?
      }
      exit(1);
   }

   // BG: Create a temporary window to set as the top window
   wxImage logoimage((const char **)AudacityLogoWithName_xpm);
   logoimage.Rescale(logoimage.GetWidth() / 2, logoimage.GetHeight() / 2);
   if( GetLayoutDirection() == wxLayout_RightToLeft)
      logoimage = logoimage.Mirror();
   wxBitmap logo(logoimage);

   AudacityProject *project;
   {
      // 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);

      wxSplashScreen temporarywindow(
         logo,
         wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_NO_TIMEOUT,
         0,
         NULL,
         wxID_ANY,
         wndRect.GetTopLeft(),
         wxDefaultSize,
         wxSTAY_ON_TOP);

      // 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.
      // We could fix this by writing our own splash screen and using Hide() 
      // until the splash scren was correctly positioned, then Show()

      // Possibly move it on to the second screen...
      temporarywindow.SetPosition( wndRect.GetTopLeft() );
      // Centered on whichever screen it is on.
      temporarywindow.Center();
      temporarywindow.SetTitle(_("Audacity is starting up..."));
      SetTopWindow(&temporarywindow);
      temporarywindow.Raise();

      // ANSWER-ME: Why is YieldFor needed at all?
      //wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI|wxEVT_CATEGORY_USER_INPUT|wxEVT_CATEGORY_UNKNOWN);
      wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);

      //JKC: Would like to put module loading here.

      // More initialization

      InitDitherers();
      AudioIO::Init();

#ifdef __WXMAC__

      // On the Mac, users don't expect a program to quit when you close the last window.
      // Create a menubar that will show when all project windows are closed.

      auto fileMenu = std::make_unique<wxMenu>();
      auto urecentMenu = std::make_unique<wxMenu>();
      auto recentMenu = urecentMenu.get();
      fileMenu->Append(wxID_NEW, wxString(_("&New")) + wxT("\tCtrl+N"));
      fileMenu->Append(wxID_OPEN, wxString(_("&Open...")) + wxT("\tCtrl+O"));
      fileMenu->AppendSubMenu(urecentMenu.release(), _("Open &Recent..."));
      fileMenu->Append(wxID_ABOUT, _("&About Audacity..."));
      fileMenu->Append(wxID_PREFERENCES, wxString(_("&Preferences...")) + wxT("\tCtrl+,"));

      {
         auto menuBar = std::make_unique<wxMenuBar>();
         menuBar->Append(fileMenu.release(), _("&File"));

         // PRL:  Are we sure wxWindows will not leak this menuBar?
         // The online documentation is not explicit.
         wxMenuBar::MacSetCommonMenuBar(menuBar.release());
      }

      auto &recentFiles = FileHistory::Global();
      recentFiles.UseMenu(recentMenu);
      recentFiles.AddFilesToMenu(recentMenu);

      SetExitOnFrameDelete(false);

#endif //__WXMAC__
      temporarywindow.Show(false);
   }

   // 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.
   {
      project = ProjectManager::New();
      wxWindow * pWnd = MakeHijackPanel();
      if (pWnd)
      {
         auto &window = GetProjectFrame( *project );
         window.Show(false);
         pWnd->SetParent( &window );
         SetTopWindow(pWnd);
         pWnd->Show(true);
      }
   }

   if( ProjectSettings::Get( *project ).GetShowSplashScreen() ){
      // 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.
      // Disabled for now, after discussion.
      // project->MayCheckForUpdates();
      SplashDialog::DoHelpWelcome(*project);
   }

   #ifdef USE_FFMPEG
   FFmpegStartup();
   #endif

   Importer::Get().Initialize();

   // Bug1561: delay the recovery dialog, to avoid crashes.
   CallAfter( [=] () mutable {
      // Remove duplicate shortcuts when there's a change of version
      int vMajorInit, vMinorInit, vMicroInit;
      gPrefs->GetVersionKeysInit(vMajorInit, vMinorInit, vMicroInit);
      if (vMajorInit != AUDACITY_VERSION || vMinorInit != AUDACITY_RELEASE
         || vMicroInit != AUDACITY_REVISION) {
         CommandManager::Get(*project).RemoveDuplicateShortcuts();
      }
      //
      // Auto-recovery
      //
      bool didRecoverAnything = false;
      if (!ShowAutoRecoveryDialogIfNeeded(&project, &didRecoverAnything))
      {
         // Important: Prevent deleting any temporary files!
         DirManager::SetDontDeleteTempFiles();
         QuitAudacity(true);
      }

      //
      // Remainder of command line parsing, but only if we didn't recover
      //
      if (!didRecoverAnything)
      {
         if (parser->Found(wxT("t")))
         {
            RunBenchmark( nullptr, ProjectSettings::Get( *project ) );
            QuitAudacity(true);
         }

         // As of wx3, there's no need to process the filename arguments as they
         // will be sent via the MacOpenFile() method.
#if !defined(__WXMAC__)
         for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++)
         {
            // PRL: Catch any exceptions, don't try this file again, continue to
            // other files.
            SafeMRUOpen(parser->GetParam(i));
         }
#endif
      }
   } );

   gInited = true;

   ModuleManager::Get().Dispatch(AppInitialized);

   mTimer.SetOwner(this, kAudacityAppTimerID);
   mTimer.Start(200);

#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 ));
         const auto pProject = GetActiveProject();
         auto pWindow = FindProjectFrame( pProject );
         GlobalPrefsDialog dialog( pWindow, pProject, factories );
         dialog.ShowModal();
         MenuCreator::RebuildAllMenuBars();
         return true;
      }
      else
         return false;
   } );
#endif

#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

   return TRUE;
}

void AudacityApp::InitCommandHandler()
{
   mCmdHandler = std::make_unique<CommandHandler>();
   //SetNextHandler(mCmdHandler);
}

// AppCommandEvent callback - just pass the event on to the CommandHandler
void AudacityApp::OnReceiveCommand(AppCommandEvent &event)
{
   wxASSERT(NULL != mCmdHandler);
   mCmdHandler->OnReceiveCommand(event);
}

void AudacityApp::OnKeyDown(wxKeyEvent &event)
{
   if(event.GetKeyCode() == WXK_ESCAPE) {
      // Stop play, including scrub, but not record
      auto project = ::GetActiveProject();
      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();
      }
   }

   event.Skip();
}

// 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 "".
   if( dir.empty() )
      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;
}

bool AudacityApp::InitTempDir()
{
   // We need to find a temp directory location.

   wxString tempFromPrefs = gPrefs->Read(wxT("/Directories/TempDir"), wxT(""));
   auto tempDefaultLoc = FileNames::DefaultTempDir();

   wxString temp;

   #ifdef __WXGTK__
   if (tempFromPrefs.length() > 0 && tempFromPrefs[0] != wxT('/'))
      tempFromPrefs = wxT("");
   #endif

   // Stop wxWidgets from printing its own error messages

   wxLogNull logNo;

   // Try temp dir that was stored in prefs first
   if( FileNames::IsTempDirectoryNameOK( tempFromPrefs ) )
      SetToExtantDirectory( temp, tempFromPrefs );

   // If that didn't work, try the default location

   if (temp.empty())
      SetToExtantDirectory( temp, tempDefaultLoc );

   // Check temp directory ownership on *nix systems only
   #ifdef __UNIX__
   struct stat tempStatBuf;
   if ( lstat(temp.mb_str(), &tempStatBuf) != 0 ) {
      temp.clear();
   }
   else {
      if ( geteuid() != tempStatBuf.st_uid ) {
         temp.clear();
      }
   }
   #endif

   if (temp.empty()) {
      // Failed
      if( !FileNames::IsTempDirectoryNameOK( tempFromPrefs ) ) {
         AudacityMessageBox(XO(
"Audacity could not find a safe place to store temporary files.\nAudacity needs a place where automatic cleanup programs won't delete the temporary files.\nPlease enter an appropriate directory in the preferences dialog."));
      } else {
         AudacityMessageBox(XO(
"Audacity could not find a place to store temporary files.\nPlease enter an appropriate directory in the preferences dialog."));
      }

      // Only want one page of the preferences
      PrefsDialog::Factories factories;
      factories.push_back(DirectoriesPrefsFactory());
      GlobalPrefsDialog dialog(nullptr, nullptr, factories);
      dialog.ShowModal();

      AudacityMessageBox(XO(
"Audacity is now going to exit. Please launch Audacity again to use the new temporary directory."));
      return false;
   }

   // The permissions don't always seem to be set on
   // some platforms.  Hopefully this fixes it...
   #ifdef __UNIX__
   chmod(OSFILENAME(temp), 0755);
   #endif

   bool bSuccess = gPrefs->Write(wxT("/Directories/TempDir"), temp) && gPrefs->Flush();
   DirManager::SetTempDir(temp);

   // Make sure the temp dir isn't locked by another process.
   if (!CreateSingleInstanceChecker(temp))
      return false;

   return bSuccess;
}

// Return true if there are no other instances of Audacity running,
// false otherwise.
//
// Use "dir" for creating lockfiles (on OS X and Unix).

bool AudacityApp::CreateSingleInstanceChecker(const wxString &dir)
{
   wxString name = wxString::Format(wxT("audacity-lock-%s"), wxGetUserId());
   mChecker.reset();
   auto checker = std::make_unique<wxSingleInstanceChecker>();

#if defined(__UNIX__)
   wxString sockFile(dir + wxT("/.audacity.sock"));
#endif

   auto runningTwoCopiesStr = XO("Running two copies of Audacity simultaneously may cause\ndata loss or cause your system to crash.\n\n");

   if (!checker->Create(name, dir)) {
      // Error initializing the wxSingleInstanceChecker.  We don't know
      // whether there is another instance running or not.

      auto prompt = XO(
"Audacity was not able to lock the temporary files directory.\nThis 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);
      if (action == wxNO)
         return false;
   }
   else if ( checker->IsAnotherRunning() ) {
      // Parse the command line to ensure correct syntax, but
      // ignore options other than -v, and only use the filenames, if any.
      auto parser = ParseCommandLine();
      if (!parser)
      {
         // Complaints have already been made
         return false;
      }

      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.
      FilePaths filenames;
      for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++)
      {
         wxFileName filename(parser->GetParam(i));
         if (filename.MakeAbsolute())
            filenames.push_back(filename.GetLongPath());
      }

#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++)
      {
         std::unique_ptr<wxConnectionBase> conn{ client.MakeConnection(wxEmptyString, IPC_APPL, IPC_TOPIC) };
         if (conn)
         {
            bool ok = false;
            if (filenames.size() > 0)
            {
               for (size_t i = 0, cnt = filenames.size(); i < cnt; i++)
               {
                  ok = conn->Execute(filenames[i]);
               }
            }
            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);

      {
         // 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++)
         {
            // Connect to the existing Audacity
            sock->Connect(addr, true);
            if (sock->IsConnected())
            {
               if (filenames.size() > 0)
               {
                  for (size_t i = 0, cnt = filenames.size(); i < cnt; i++)
                  {
                     const wxString param = filenames[i];
                     sock->WriteMsg((const wxChar *) param, (param.length() + 1) * sizeof(wxChar));
                  }
               }
               else
               {
                  // Send an empty string to force existing Audacity to front
                  sock->WriteMsg(wxEmptyString, sizeof(wxChar));
               }

               return sock->Error();
            }

            wxMilliSleep(100);
         }
      }
#endif
      // There is another copy of Audacity running.  Force quit.

      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\nprocess to open multiple projects simultaneously.\n");
      AudacityMessageBox(
         prompt, XO("Audacity is already running"),
         wxOK | wxICON_ERROR);

#ifdef __WXMAC__
      // Bug 2052
      // On mac, the lock file may persist and stop Audacity starting properly.
      auto lockFileName = wxFileName(dir,name);
      bool bIsLocked = lockFileName.IsOk() && lockFileName.FileExists();
      if( bIsLocked ){
         int action = AudacityMessageBox(
XO("If you're sure another copy of Audacity isn't\nrunning, Audacity can skip the test for\n'Audacity already running' next time\nby removing the lock file:\n\n%s\n\nDo you want to do that?")
               .Format(lockFileName.GetFullName()),
            XO("Possible Lock File Problem"),
            wxYES_NO | wxICON_EXCLAMATION,
            NULL);
         if (action == wxYES){
            // If locked, unlock.
            lockFileName.SetPermissions( wxS_DEFAULT );
            ::wxRemoveFile( lockFileName.GetFullName() );
         }
      }
#endif
      return false;
   }

#if defined(__WXMSW__)
   // Create the DDE IPC server
   mIPCServ = std::make_unique<IPCServ>(IPC_APPL);
#else
   int mask = umask(077);
   remove(OSFILENAME(sockFile));
   wxUNIXaddress addr;
   addr.Filename(sockFile);
   mIPCServ = std::make_unique<wxSocketServer>(addr, wxSOCKET_NOWAIT);
   umask(mask);

   if (!mIPCServ || !mIPCServ->IsOk())
   {
      // TODO:  Complain here
      return false;
   }

   mIPCServ->SetEventHandler(*this, ID_IPC_SERVER);
   mIPCServ->SetNotify(wxSOCKET_CONNECTION_FLAG);
   mIPCServ->Notify(true);
#endif
   mChecker = std::move(checker);
   return true;
}

#if defined(__UNIX__)
void AudacityApp::OnServerEvent(wxSocketEvent & /* evt */)
{
   wxSocketBase *sock;

   // Accept all pending connection requests
   do
   {
      sock = mIPCServ->Accept(false);
      if (sock)
      {
         // Setup the socket
         sock->SetEventHandler(*this, ID_IPC_SOCKET);
         sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
         sock->Notify(true);
      }
   } while (sock);
}

void AudacityApp::OnSocketEvent(wxSocketEvent & evt)
{
   wxSocketBase *sock = evt.GetSocket();

   if (evt.GetSocketEvent() == wxSOCKET_LOST)
   {
      sock->Destroy();
      return;
   }

   // Read the length of the filename and bail if we have a short read
   wxChar name[PATH_MAX];
   sock->ReadMsg(&name, sizeof(name));
   if (!sock->Error())
   {
      // Add the filename to the queue.  It will be opened by
      // the OnTimer() event when it is safe to do so.
      ofqueue.push_back(name);
   }
}

#endif

std::unique_ptr<wxCmdLineParser> AudacityApp::ParseCommandLine()
{
   auto parser = std::make_unique<wxCmdLineParser>(argc, argv);
   if (!parser)
   {
      return nullptr;
   }

   /*i18n-hint: This controls the number of bytes that Audacity will
    *           use when writing files to the disk */
   parser->AddOption(wxT("b"), wxT("blocksize"), _("set max disk block size in bytes"),
                     wxCMD_LINE_VAL_NUMBER);

   /*i18n-hint: This decodes an autosave file */
   parser->AddOption(wxT("d"), wxT("decode"), _("decode an autosave file"),
                     wxCMD_LINE_VAL_STRING);

   /*i18n-hint: This displays a list of available options */
   parser->AddSwitch(wxT("h"), wxT("help"), _("this help message"),
                     wxCMD_LINE_OPTION_HELP);

   /*i18n-hint: This runs a set of automatic tests on Audacity itself */
   parser->AddSwitch(wxT("t"), wxT("test"), _("run self diagnostics"));

   /*i18n-hint: This displays the Audacity version */
   parser->AddSwitch(wxT("v"), wxT("version"), _("display Audacity version"));

   /*i18n-hint: This is a list of one or more files that Audacity
    *           should open upon startup */
   parser->AddParam(_("audio or project file name"),
                    wxCMD_LINE_VAL_STRING,
                    wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL);

   // Run the parser
   if (parser->Parse() == 0)
      return parser;

   return{};
}

void AudacityApp::OnQueryEndSession(wxCloseEvent & event)
{
   bool mustVeto = false;

#ifdef __WXMAC__
   mustVeto = wxDialog::OSXHasModalDialogsOpen();
#endif

   if ( mustVeto )
      event.Veto(true);
   else
      OnEndSession(event);
}

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.
   gIsQuitting = true;
   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();
   bool closedAll = AllProjects::Close( force );
   if ( !closedAll )
   {
      gIsQuitting = false;
      event.Veto();
   }
}

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();
      }
   }

   FileHistory::Global().Save(*gPrefs, wxT("RecentFiles"));

   FinishPreferences();

#ifdef USE_FFMPEG
   DropFFmpegLibs();
#endif

   DeinitFFT();

   AudioIO::Deinit();

   MenuTable::DestroyRegistry();

   // 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.)

void AudacityApp::OnMenuAbout(wxCommandEvent & /*event*/)
{
   // This function shadows a similar function
   // in Menus.cpp, but should only be used on the Mac platform.
#ifdef __WXMAC__
   // 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
      (safenew AboutDialog{ nullptr })->Show(true);
#else
      wxASSERT(false);
#endif
}

void AudacityApp::OnMenuNew(wxCommandEvent & event)
{
   // This function shadows a similar function
   // in Menus.cpp, but should only be used on the Mac platform
   // when no project windows are open. This check assures that
   // this happens, and enable the same code to be present on
   // all platforms.

   if(AllProjects{}.empty())
      (void) ProjectManager::New();
   else
      event.Skip();
}


void AudacityApp::OnMenuOpen(wxCommandEvent & event)
{
   // This function shadows a similar function
   // in Menus.cpp, but should only be used on the Mac platform
   // when no project windows are open. This check assures that
   // this happens, and enable the same code to be present on
   // all platforms.


   if(AllProjects{}.empty())
      ProjectManager::OpenFiles(NULL);
   else
      event.Skip();


}

void AudacityApp::OnMenuPreferences(wxCommandEvent & event)
{
   // This function shadows a similar function
   // in Menus.cpp, but should only be used on the Mac platform
   // when no project windows are open. This check assures that
   // this happens, and enable the same code to be present on
   // all platforms.

   if(AllProjects{}.empty()) {
      GlobalPrefsDialog dialog(nullptr /* parent */, nullptr );
      dialog.ShowModal();
   }
   else
      event.Skip();

}

void AudacityApp::OnMenuExit(wxCommandEvent & event)
{
   // This function shadows a similar function
   // in Menus.cpp, but should only be used on the Mac platform
   // when no project windows are open. This check assures that
   // this happens, and enable the same code to be present on
   // all platforms.

   // LL:  Removed "if" to allow closing based on final project count.
   // if(AllProjects{}.empty())
      QuitAudacity();

   // LL:  Veto quit if projects are still open.  This can happen
   //      if the user selected Cancel in a Save dialog.
   event.Skip(AllProjects{}.empty());

}

//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 =
            AudacityMessageBox(
               XO(
"Audacity project (.AUP) files are not currently \nassociated with Audacity. \n\nAssociate them, so they open on double-click?"),
               XO("Audacity Project Files"),
               wxYES_NO | wxICON_QUESTION);
         if (wantAssoc == wxYES) {
            gPrefs->Write(wxT("/WantAssociateFiles"), true);
            gPrefs->Flush();

            wxString root_key;

            root_key = wxT("HKCU\\Software\\Classes\\");
            associateFileTypes.SetName(root_key + wxT(".AUP")); // Start again with HKEY_CLASSES_ROOT.
            if (!associateFileTypes.Create(true)) {
               // Not at HKEY_CLASSES_USER. Try HKEY_CURRENT_ROOT.
               root_key = wxT("HKCR\\");
               associateFileTypes.SetName(root_key + wxT(".AUP"));
               if (!associateFileTypes.Create(true)) {
                  // Actually, can't create keys. Empty root_key to flag failure.
                  root_key.Empty();
               }
            }
            if (root_key.empty()) {
               //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

