Audacity  2.2.2
AudacityApp.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  AudacityApp.cpp
6 
7  Dominic Mazzoni
8 
9 ******************************************************************//*******************************************************************/
17 
18 #if 0
19 // This may be used to debug memory leaks.
20 // See: Visual Leak Dectector @ http://vld.codeplex.com/
21 #include <vld.h>
22 #endif
23 
24 #include "Audacity.h" // This should always be included first
25 #include "AudacityApp.h"
27 
28 #include <wx/defs.h>
29 #include <wx/app.h>
30 #include <wx/bitmap.h>
31 #include <wx/docview.h>
32 #include <wx/event.h>
33 #include <wx/ipc.h>
34 #include <wx/log.h>
35 #include <wx/window.h>
36 #include <wx/intl.h>
37 #include <wx/menu.h>
38 #include <wx/snglinst.h>
39 #include <wx/splash.h>
40 #include <wx/stdpaths.h>
41 #include <wx/sysopt.h>
42 #include <wx/fontmap.h>
43 
44 #include <wx/fs_zip.h>
45 #include <wx/image.h>
46 
47 #include <wx/dir.h>
48 #include <wx/file.h>
49 #include <wx/filename.h>
50 
51 #ifdef __WXGTK__
52 #include <unistd.h>
53 #endif
54 
55 // chmod, lstat, geteuid
56 #ifdef __UNIX__
57 #include <sys/types.h>
58 #include <sys/file.h>
59 #include <sys/stat.h>
60 #endif
61 
62 #include "AudacityException.h"
63 #include "AudacityLogger.h"
64 #include "AboutDialog.h"
65 #include "AColor.h"
66 #include "AudioIO.h"
67 #include "Benchmark.h"
68 #include "DirManager.h"
72 #include "effects/Contrast.h"
73 #include "widgets/ASlider.h"
74 #include "FFmpeg.h"
75 #include "Internat.h"
76 #include "LangChoice.h"
77 #include "Languages.h"
78 #include "PluginManager.h"
79 #include "Prefs.h"
80 #include "Project.h"
81 #include "Screenshot.h"
82 #include "Sequence.h"
83 #include "WaveTrack.h"
84 #include "prefs/PrefsDialog.h"
85 #include "Theme.h"
86 #include "PlatformCompatibility.h"
87 #include "FileNames.h"
88 #include "AutoRecovery.h"
89 #include "SplashDialog.h"
90 #include "FFT.h"
91 #include "BlockFile.h"
92 #include "ondemand/ODManager.h"
93 #include "commands/Keyboard.h"
94 #include "widgets/ErrorDialog.h"
95 #include "prefs/DirectoriesPrefs.h"
96 #include "tracks/ui/Scrubbing.h"
97 
98 //temporarilly commented out till it is added to all projects
99 //#include "Profiler.h"
100 
101 #include "ModuleManager.h"
102 
103 #include "import/Import.h"
104 
105 #include "Experimental.h"
106 
107 #if defined(EXPERIMENTAL_CRASH_REPORT)
108 #include <wx/debugrpt.h>
109 #include <wx/evtloop.h>
110 #include <wx/textdlg.h>
111 #endif
112 
113 #ifdef EXPERIMENTAL_SCOREALIGN
115 #endif
116 
117 #if 0
118 #ifdef _DEBUG
119  #ifdef _MSC_VER
120  #undef THIS_FILE
121  static char*THIS_FILE= __FILE__;
122  #define new new(_NORMAL_BLOCK, THIS_FILE, __LINE__)
123  #endif
124 #endif
125 #endif
126 
127 // Windows specific linker control...only needed once so
128 // this is a good place (unless we want to add another file).
129 #if defined(__WXMSW__)
130 //#if wxCHECK_VERSION(3, 0, 2) && !wxCHECK_VERSION(3, 1, 0)
131 #include <wx/init.h>
132 //#endif
133 // These lines ensure that Audacity gets WindowsXP themes.
134 // Without them we get the old-style Windows98/2000 look under XP.
135 # if !defined(__WXWINCE__)
136 # pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
137 # endif
138 
139 // These lines allows conditional inclusion of the various libraries
140 // that Audacity can use.
141 
142 # if defined(USE_LIBFLAC)
143 # pragma comment(lib, "libflac++")
144 # pragma comment(lib, "libflac")
145 # endif
146 
147 # if defined(USE_LIBID3TAG)
148 # pragma comment(lib, "libid3tag")
149 # endif
150 
151 # if defined(USE_LIBMAD)
152 # pragma comment(lib, "libmad")
153 # endif
154 
155 # if defined(USE_LIBTWOLAME)
156 # pragma comment(lib, "twolame")
157 # endif
158 
159 # if defined(USE_LIBVORBIS)
160 # pragma comment(lib, "libogg")
161 # pragma comment(lib, "libvorbis")
162 # endif
163 
164 # if defined(USE_LV2)
165 # pragma comment(lib, "lv2")
166 # endif
167 
168 # if defined(USE_MIDI)
169 # pragma comment(lib, "portsmf")
170 # endif
171 
172 # if defined(EXPERIMENTAL_MIDI_OUT)
173 # pragma comment(lib, "portmidi")
174 # endif
175 
176 # if defined(EXPERIMENTAL_SCOREALIGN)
177 # pragma comment(lib, "libscorealign")
178 # endif
179 
180 # if defined(USE_NYQUIST)
181 # pragma comment(lib, "libnyquist")
182 # endif
183 
184 # if defined(USE_PORTMIXER)
185 # pragma comment(lib, "portmixer")
186 # endif
187 
188 # if defined(USE_SBSMS)
189 # pragma comment(lib, "sbsms")
190 # endif
191 
192 # if defined(USE_SOUNDTOUCH)
193 # pragma comment(lib, "soundtouch")
194 # endif
195 
196 # if defined(USE_VAMP)
197 # pragma comment(lib, "libvamp")
198 # endif
199 
200 # if defined(_DEBUG)
201 # define D "d"
202 # else
203 # define D ""
204 # endif
205 # if wxCHECK_VERSION(3, 1, 0)
206 # define V "31"
207 # elif wxCHECK_VERSION(3, 0, 0)
208 # define V "30"
209 # else
210 # define V "28"
211 # endif
212 
213 # if defined(EXPERIMENTAL_CRASH_REPORT)
214 # pragma comment(lib, "wxmsw" V "u" D "_qa")
215 # endif
216 # pragma comment(lib, "wxbase" V "u" D)
217 # pragma comment(lib, "wxbase" V "u" D "_net")
218 # pragma comment(lib, "wxmsw" V "u" D "_adv")
219 # pragma comment(lib, "wxmsw" V "u" D "_core")
220 # pragma comment(lib, "wxmsw" V "u" D "_html")
221 # pragma comment(lib, "wxpng" D)
222 # pragma comment(lib, "wxzlib" D)
223 # pragma comment(lib, "wxjpeg" D)
224 # pragma comment(lib, "wxtiff" D)
225 
226 # undef V
227 # undef D
228 
229 #endif //(__WXMSW__)
230 
231 // DA: Logo for Splash Screen
232 #ifdef EXPERIMENTAL_DA
233 #include "../images/DarkAudacityLogoWithName.xpm"
234 #else
235 #include "../images/AudacityLogoWithName.xpm"
236 #endif
237 
238 
242 
243 DEFINE_EVENT_TYPE(EVT_OPEN_AUDIO_FILE);
244 wxDEFINE_EVENT(EVT_LANGUAGE_CHANGE, wxCommandEvent);
245 
246 #if 0
247 #ifdef __WXGTK__
248 static void wxOnAssert(const wxChar *fileName, int lineNumber, const wxChar *msg)
249 {
250  if (msg)
251  wxPrintf("ASSERTION FAILED: %s\n%s: %d\n", (const char *)wxString(msg).mb_str(), (const char *)wxString(fileName).mb_str(), lineNumber);
252  else
253  wxPrintf("ASSERTION FAILED!\n%s: %d\n", (const char *)wxString(fileName).mb_str(), lineNumber);
254 
255  // Force core dump
256  int *i = 0;
257  if (*i)
258  exit(1);
259 
260  exit(0);
261 }
262 #endif
263 #endif
264 
265 static bool gInited = false;
266 bool gIsQuitting = false;
267 
268 void QuitAudacity(bool bForce)
269 {
270  if (gIsQuitting)
271  return;
272 
273  gIsQuitting = true;
274 
275  wxTheApp->SetExitOnFrameDelete(true);
276 
277  // Try to close each open window. If the user hits Cancel
278  // in a Save Changes dialog, don't continue.
279  // BG: unless force is true
280 
281  // BG: Are there any projects open?
282  //- if (!gAudacityProjects.IsEmpty())
283 /*start+*/
284  if (gAudacityProjects.empty())
285  {
286 #ifdef __WXMAC__
288 #endif
289  }
290  else
291 /*end+*/
292  {
293  SaveWindowSize();
294  while (gAudacityProjects.size())
295  {
296  // Closing the project has global side-effect
297  // of deletion from gAudacityProjects
298  if (bForce)
299  {
300  gAudacityProjects[0]->Close(true);
301  }
302  else
303  {
304  if (!gAudacityProjects[0]->Close())
305  {
306  gIsQuitting = false;
307  return;
308  }
309  }
310  }
311  }
312 
314 
315 #ifdef EXPERIMENTAL_SCOREALIGN
317 #endif
319 
320  //release ODManager Threads
321  ODManager::Quit();
322 
323  //print out profile if we have one by deleting it
324  //temporarilly commented out till it is added to all projects
325  //DELETE Profiler::Instance();
326 
327  //remove our logger
328  std::unique_ptr<wxLog>{ wxLog::SetActiveTarget(NULL) }; // DELETE
329 
330  if (bForce)
331  {
332  wxExit();
333  }
334 }
335 
337 {
338  QuitAudacity(false);
339 }
340 
342 {
343  if (wxGetApp().GetWindowRectAlreadySaved())
344  {
345  return;
346  }
347  bool validWindowForSaveWindowSize = FALSE;
348  AudacityProject * validProject = NULL;
349  bool foundIconizedProject = FALSE;
350  size_t numProjects = gAudacityProjects.size();
351  for (size_t i = 0; i < numProjects; i++)
352  {
353  if (!gAudacityProjects[i]->IsIconized()) {
354  validWindowForSaveWindowSize = TRUE;
355  validProject = gAudacityProjects[i].get();
356  i = numProjects;
357  }
358  else
359  foundIconizedProject = TRUE;
360 
361  }
362  if (validWindowForSaveWindowSize)
363  {
364  wxRect windowRect = validProject->GetRect();
365  wxRect normalRect = validProject->GetNormalizedWindowState();
366  bool wndMaximized = validProject->IsMaximized();
367  gPrefs->Write(wxT("/Window/X"), windowRect.GetX());
368  gPrefs->Write(wxT("/Window/Y"), windowRect.GetY());
369  gPrefs->Write(wxT("/Window/Width"), windowRect.GetWidth());
370  gPrefs->Write(wxT("/Window/Height"), windowRect.GetHeight());
371  gPrefs->Write(wxT("/Window/Maximized"), wndMaximized);
372  gPrefs->Write(wxT("/Window/Normal_X"), normalRect.GetX());
373  gPrefs->Write(wxT("/Window/Normal_Y"), normalRect.GetY());
374  gPrefs->Write(wxT("/Window/Normal_Width"), normalRect.GetWidth());
375  gPrefs->Write(wxT("/Window/Normal_Height"), normalRect.GetHeight());
376  gPrefs->Write(wxT("/Window/Iconized"), FALSE);
377  }
378  else
379  {
380  if (foundIconizedProject) {
381  validProject = gAudacityProjects[0].get();
382  bool wndMaximized = validProject->IsMaximized();
383  wxRect normalRect = validProject->GetNormalizedWindowState();
384  // store only the normal rectangle because the itemized rectangle
385  // makes no sense for an opening project window
386  gPrefs->Write(wxT("/Window/X"), normalRect.GetX());
387  gPrefs->Write(wxT("/Window/Y"), normalRect.GetY());
388  gPrefs->Write(wxT("/Window/Width"), normalRect.GetWidth());
389  gPrefs->Write(wxT("/Window/Height"), normalRect.GetHeight());
390  gPrefs->Write(wxT("/Window/Maximized"), wndMaximized);
391  gPrefs->Write(wxT("/Window/Normal_X"), normalRect.GetX());
392  gPrefs->Write(wxT("/Window/Normal_Y"), normalRect.GetY());
393  gPrefs->Write(wxT("/Window/Normal_Width"), normalRect.GetWidth());
394  gPrefs->Write(wxT("/Window/Normal_Height"), normalRect.GetHeight());
395  gPrefs->Write(wxT("/Window/Iconized"), TRUE);
396  }
397  else {
398  // this would be a very strange case that might possibly occur on the Mac
399  // Audacity would have to be running with no projects open
400  // in this case we are going to write only the default values
401  wxRect defWndRect;
402  GetDefaultWindowRect(&defWndRect);
403  gPrefs->Write(wxT("/Window/X"), defWndRect.GetX());
404  gPrefs->Write(wxT("/Window/Y"), defWndRect.GetY());
405  gPrefs->Write(wxT("/Window/Width"), defWndRect.GetWidth());
406  gPrefs->Write(wxT("/Window/Height"), defWndRect.GetHeight());
407  gPrefs->Write(wxT("/Window/Maximized"), FALSE);
408  gPrefs->Write(wxT("/Window/Normal_X"), defWndRect.GetX());
409  gPrefs->Write(wxT("/Window/Normal_Y"), defWndRect.GetY());
410  gPrefs->Write(wxT("/Window/Normal_Width"), defWndRect.GetWidth());
411  gPrefs->Write(wxT("/Window/Normal_Height"), defWndRect.GetHeight());
412  gPrefs->Write(wxT("/Window/Iconized"), FALSE);
413  }
414  }
415  gPrefs->Flush();
417 }
418 
419 #if defined(__WXGTK__) && defined(HAVE_GTK)
420 
422 // Provide the ability to receive notification from the session manager
423 // when the user is logging out or shutting down.
424 //
425 // Most of this was taken from nsNativeAppSupportUnix.cpp from Mozilla.
427 
428 // TODO: May need updating. Is this code too obsolete (relying on Gnome2 so's) to be
429 // worth keeping anymore?
430 // CB suggests we use libSM directly ref:
431 // http://www.x.org/archive/X11R7.7/doc/libSM/SMlib.html#The_Save_Yourself_Callback
432 
433 #include <dlfcn.h>
434 /* There is a conflict between the type names used in Glib >= 2.21 and those in
435  * wxGTK (http://trac.wxwidgets.org/ticket/10883)
436  * Happily we can avoid the hack, as we only need some of the headers, not
437  * the full GTK headers
438  */
439 #include <glib-object.h>
440 
441 typedef struct _GnomeProgram GnomeProgram;
442 typedef struct _GnomeModuleInfo GnomeModuleInfo;
443 typedef struct _GnomeClient GnomeClient;
444 
445 typedef enum
446 {
447  GNOME_SAVE_GLOBAL,
448  GNOME_SAVE_LOCAL,
449  GNOME_SAVE_BOTH
450 } GnomeSaveStyle;
451 
452 typedef enum
453 {
454  GNOME_INTERACT_NONE,
455  GNOME_INTERACT_ERRORS,
456  GNOME_INTERACT_ANY
457 } GnomeInteractStyle;
458 
459 typedef enum
460 {
461  GNOME_DIALOG_ERROR,
462  GNOME_DIALOG_NORMAL
463 } GnomeDialogType;
464 
465 typedef GnomeProgram * (*_gnome_program_init_fn)(const char *,
466  const char *,
467  const GnomeModuleInfo *,
468  int,
469  char **,
470  const char *,
471  ...);
472 typedef const GnomeModuleInfo * (*_libgnomeui_module_info_get_fn)();
473 typedef GnomeClient * (*_gnome_master_client_fn)(void);
474 typedef void (*GnomeInteractFunction)(GnomeClient *,
475  gint,
476  GnomeDialogType,
477  gpointer);
478 typedef void (*_gnome_client_request_interaction_fn)(GnomeClient *,
479  GnomeDialogType,
480  GnomeInteractFunction,
481  gpointer);
482 typedef void (*_gnome_interaction_key_return_fn)(gint, gboolean);
483 
484 static _gnome_client_request_interaction_fn gnome_client_request_interaction;
485 static _gnome_interaction_key_return_fn gnome_interaction_key_return;
486 
487 static void interact_cb(GnomeClient * /* client */,
488  gint key,
489  GnomeDialogType /* type */,
490  gpointer /* data */)
491 {
492  wxCloseEvent e(wxEVT_QUERY_END_SESSION, wxID_ANY);
493  e.SetEventObject(&wxGetApp());
494  e.SetCanVeto(true);
495 
496  wxGetApp().ProcessEvent(e);
497 
498  gnome_interaction_key_return(key, e.GetVeto());
499 }
500 
501 static gboolean save_yourself_cb(GnomeClient *client,
502  gint /* phase */,
503  GnomeSaveStyle /* style */,
504  gboolean shutdown,
505  GnomeInteractStyle interact,
506  gboolean /* fast */,
507  gpointer /* user_data */)
508 {
509  if (!shutdown || interact != GNOME_INTERACT_ANY) {
510  return TRUE;
511  }
512 
513  if (gAudacityProjects.empty()) {
514  return TRUE;
515  }
516 
517  gnome_client_request_interaction(client,
518  GNOME_DIALOG_NORMAL,
519  interact_cb,
520  NULL);
521 
522  return TRUE;
523 }
524 
525 class GnomeShutdown
526 {
527  public:
528  GnomeShutdown()
529  {
530  mArgv[0].reset(strdup("Audacity"));
531 
532  mGnomeui = dlopen("libgnomeui-2.so.0", RTLD_NOW);
533  if (!mGnomeui) {
534  return;
535  }
536 
537  mGnome = dlopen("libgnome-2.so.0", RTLD_NOW);
538  if (!mGnome) {
539  return;
540  }
541 
542  _gnome_program_init_fn gnome_program_init = (_gnome_program_init_fn)
543  dlsym(mGnome, "gnome_program_init");
544  _libgnomeui_module_info_get_fn libgnomeui_module_info_get = (_libgnomeui_module_info_get_fn)
545  dlsym(mGnomeui, "libgnomeui_module_info_get");
546  _gnome_master_client_fn gnome_master_client = (_gnome_master_client_fn)
547  dlsym(mGnomeui, "gnome_master_client");
548 
549  gnome_client_request_interaction = (_gnome_client_request_interaction_fn)
550  dlsym(mGnomeui, "gnome_client_request_interaction");
551  gnome_interaction_key_return = (_gnome_interaction_key_return_fn)
552  dlsym(mGnomeui, "gnome_interaction_key_return");
553 
554 
555  if (!gnome_program_init || !libgnomeui_module_info_get) {
556  return;
557  }
558 
559  gnome_program_init(mArgv[0].get(),
560  "1.0",
561  libgnomeui_module_info_get(),
562  1,
563  reinterpret_cast<char**>(mArgv),
564  NULL);
565 
566  mClient = gnome_master_client();
567  if (mClient == NULL) {
568  return;
569  }
570 
571  g_signal_connect(mClient, "save-yourself", G_CALLBACK(save_yourself_cb), NULL);
572  }
573 
574  virtual ~GnomeShutdown()
575  {
576  // Do not dlclose() the libraries here lest you want segfaults...
577  }
578 
579  private:
580 
581  MallocString<> mArgv[1];
582  void *mGnomeui;
583  void *mGnome;
584  GnomeClient *mClient;
585 };
586 
587 // This variable exists to call the constructor and
588 // connect a signal for the 'save-yourself' message.
589 GnomeShutdown GnomeShutdownInstance;
590 
591 #endif
592 
593 // Where drag/drop or "Open With" filenames get stored until
594 // the timer routine gets around to picking them up.
595 static wxArrayString ofqueue;
596 
597 //
598 // DDE support for opening multiple files with one instance
599 // of Audacity.
600 //
601 
602 #define IPC_APPL wxT("audacity")
603 #define IPC_TOPIC wxT("System")
604 
605 class IPCConn final : public wxConnection
606 {
607 public:
609  : wxConnection()
610  {
611  };
612 
614  {
615  };
616 
617  bool OnExec(const wxString & WXUNUSED(topic),
618  const wxString & data)
619  {
620  // Add the filename to the queue. It will be opened by
621  // the OnTimer() event when it is safe to do so.
622  ofqueue.Add(data);
623 
624  return true;
625  }
626 };
627 
628 class IPCServ final : public wxServer
629 {
630 public:
631  IPCServ(const wxString & appl)
632  : wxServer()
633  {
634  Create(appl);
635  };
636 
638  {
639  };
640 
641  wxConnectionBase *OnAcceptConnection(const wxString & topic) override
642  {
643  if (topic != IPC_TOPIC) {
644  return NULL;
645  }
646 
647  // Trust wxWidgets framework to DELETE it
648  return safenew IPCConn();
649  };
650 };
651 
652 #if defined(__WXMAC__)
653 
654 IMPLEMENT_APP_NO_MAIN(AudacityApp)
655 IMPLEMENT_WX_THEME_SUPPORT
656 
657 int main(int argc, char *argv[])
658 {
659  wxDISABLE_DEBUG_SUPPORT();
660 
661  return wxEntry(argc, argv);
662 }
663 
664 #elif defined(__WXMSW__) && !wxCHECK_VERSION(3, 1, 0)
665 // Disable telling Windows that we support HiDPI displays. It is forced on
666 // in wxWidget versions between 3.0.0 and 3.1.0.
667 IMPLEMENT_APP_NO_MAIN(AudacityApp)
668 IMPLEMENT_WX_THEME_SUPPORT
669 
670 extern "C" int WINAPI WinMain(HINSTANCE hInstance,
671  HINSTANCE hPrevInstance,
672  wxCmdLineArgType WXUNUSED(lpCmdLine),
673  int nCmdShow)
674 {
675  wxDISABLE_DEBUG_SUPPORT();
676 
677  // Disable setting of HiDPI aware mode
678  wxMSWDisableSettingHighDPIAware();
679 
680  /* NB: We pass NULL in place of lpCmdLine to behave the same as */
681  /* Borland-specific wWinMain() above. If it becomes needed */
682  /* to pass lpCmdLine to wxEntry() here, you'll have to fix */
683  /* wWinMain() above too. */
684  return wxEntry(hInstance, hPrevInstance, NULL, nCmdShow);
685 }
686 
687 #else
688 IMPLEMENT_APP(AudacityApp)
689 #endif
690 
691 #ifdef __WXMAC__
692 
693 // in response of an open-document apple event
694 void AudacityApp::MacOpenFile(const wxString &fileName)
695 {
696  ofqueue.Add(fileName);
697 }
698 
699 // in response of a print-document apple event
700 void AudacityApp::MacPrintFile(const wxString &fileName)
701 {
702  ofqueue.Add(fileName);
703 }
704 
705 // in response of a open-application apple event
706 void AudacityApp::MacNewFile()
707 {
708  if (!gInited)
709  return;
710 
711  // This method should only be used on the Mac platform
712  // when no project windows are open.
713 
714  if (gAudacityProjects.size() == 0) {
716  }
717 }
718 
719 #endif //__WXMAC__
720 
721 #define ID_RECENT_CLEAR 6100
722 #define ID_RECENT_FIRST 6101
723 #define ID_RECENT_LAST 6112
724 
725 // IPC communication
726 #define ID_IPC_SERVER 6200
727 #define ID_IPC_SOCKET 6201
728 
729 // we don't really care about the timer id, but set this value just in case we do in the future
730 #define kAudacityAppTimerID 0
731 
732 BEGIN_EVENT_TABLE(AudacityApp, wxApp)
733  EVT_QUERY_END_SESSION(AudacityApp::OnQueryEndSession)
734  EVT_END_SESSION(AudacityApp::OnEndSession)
735 
736  EVT_TIMER(kAudacityAppTimerID, AudacityApp::OnTimer)
737 #ifdef __WXMAC__
738  EVT_MENU(wxID_NEW, AudacityApp::OnMenuNew)
739  EVT_MENU(wxID_OPEN, AudacityApp::OnMenuOpen)
740  EVT_MENU(wxID_ABOUT, AudacityApp::OnMenuAbout)
741  EVT_MENU(wxID_PREFERENCES, AudacityApp::OnMenuPreferences)
742  EVT_MENU(wxID_EXIT, AudacityApp::OnMenuExit)
743 #endif
744 
745 #ifndef __WXMSW__
747  EVT_SOCKET(ID_IPC_SOCKET, AudacityApp::OnSocketEvent)
748 #endif
749 
750  // Recent file event handlers.
752  EVT_MENU_RANGE(ID_RECENT_FIRST, ID_RECENT_LAST, AudacityApp::OnMRUFile)
753 
754  // Handle AppCommandEvents (usually from a script)
755  EVT_APP_COMMAND(wxID_ANY, AudacityApp::OnReceiveCommand)
756 
757  // Global ESC key handling
758  EVT_KEY_DOWN(AudacityApp::OnKeyDown)
760 
761 // backend for OnMRUFile
762 // TODO: Would be nice to make this handle not opening a file with more panache.
763 // - Inform the user if DefaultOpenPath not set.
764 // - Switch focus to correct instance of project window, if already open.
765 bool AudacityApp::MRUOpen(const wxString &fullPathStr) {
766  // Most of the checks below are copied from AudacityProject::OpenFiles.
767  // - some rationalisation might be possible.
768 
770 
771  if (!fullPathStr.IsEmpty())
772  {
773  // verify that the file exists
774  if (wxFile::Exists(fullPathStr))
775  {
777 
778  // Make sure it isn't already open.
779  // Test here even though AudacityProject::OpenFile() also now checks, because
780  // that method does not return the bad result.
781  // That itself may be a FIXME.
782  if (AudacityProject::IsAlreadyOpen(fullPathStr))
783  return false;
784 
785  // DMM: If the project is dirty, that means it's been touched at
786  // all, and it's not safe to open a NEW project directly in its
787  // place. Only if the project is brand-NEW clean and the user
788  // hasn't done any action at all is it safe for Open to take place
789  // inside the current project.
790  //
791  // If you try to Open a NEW project inside the current window when
792  // there are no tracks, but there's an Undo history, etc, then
793  // bad things can happen, including data files moving to the NEW
794  // project directory, etc.
795  if (proj && (proj->GetDirty() || !proj->GetIsEmpty()))
796  proj = nullptr;
797  // This project is clean; it's never been touched. Therefore
798  // all relevant member variables are in their initial state,
799  // and it's okay to open a NEW project inside this window.
800  AudacityProject::OpenProject( proj, fullPathStr );
801  }
802  else {
803  // File doesn't exist - remove file from history
804  AudacityMessageBox(wxString::Format(_("%s could not be found.\n\nIt has been removed from the list of recent files."),
805  fullPathStr));
806  return(false);
807  }
808  }
809  return(true);
810 }
811 
812 bool AudacityApp::SafeMRUOpen(const wxString &fullPathStr)
813 {
814  return GuardedCall< bool >( [&]{ return MRUOpen( fullPathStr ); } );
815 }
816 
817 void AudacityApp::OnMRUClear(wxCommandEvent& WXUNUSED(event))
818 {
819  mRecentFiles->Clear();
820 }
821 
822 //vvv Basically, anything from Recent Files is treated as a .aup, until proven otherwise,
823 // then it tries to Import(). Very questionable handling, imo.
824 // Better, for example, to check the file type early on.
825 void AudacityApp::OnMRUFile(wxCommandEvent& event) {
826  int n = event.GetId() - ID_RECENT_FIRST;
827  const wxString &fullPathStr = mRecentFiles->GetHistoryFile(n);
828 
829  // Try to open only if not already open.
830  // Test IsAlreadyOpen() here even though AudacityProject::MRUOpen() also now checks,
831  // because we don't want to RemoveFileFromHistory() just because it already exists,
832  // and AudacityApp::OnMacOpenFile() calls MRUOpen() directly.
833  // that method does not return the bad result.
834  // PRL: Don't call SafeMRUOpen
835  // -- if open fails for some exceptional reason of resource exhaustion that
836  // the user can correct, leave the file in history.
837  if (!AudacityProject::IsAlreadyOpen(fullPathStr) && !MRUOpen(fullPathStr))
838  mRecentFiles->RemoveFileFromHistory(n);
839 }
840 
841 void AudacityApp::OnTimer(wxTimerEvent& WXUNUSED(event))
842 {
843  // Filenames are queued when Audacity receives a few of the
844  // AppleEvent messages (via wxWidgets). So, open any that are
845  // in the queue and clean the queue.
846  if (gInited) {
847  if (ofqueue.GetCount()) {
848  // Load each file on the queue
849  while (ofqueue.GetCount()) {
850  wxString name;
851  name.swap(ofqueue[0]);
852  ofqueue.RemoveAt(0);
853 
854  // Get the user's attention if no file name was specified
855  if (name.IsEmpty()) {
856  // Get the users attention
857  AudacityProject *project = GetActiveProject();
858  if (project) {
859  project->Maximize();
860  project->Raise();
861  project->RequestUserAttention();
862  }
863  continue;
864  }
865 
866  // TODO: Handle failures better.
867  // Some failures are OK, e.g. file not found, just would-be-nices to do better,
868  // so FAIL_MSG is more a case of an enhancement request than an actual problem.
869  // LL: In all but one case an appropriate message is already displayed. The
870  // instance that a message is NOT displayed is when a failure to write
871  // to the config file has occurred.
872  // PRL: Catch any exceptions, don't try this file again, continue to
873  // other files.
874  if (!SafeMRUOpen(name)) {
875  wxFAIL_MSG(wxT("MRUOpen failed"));
876  }
877  }
878  }
879  }
880 
881  // Check if a warning for missing aliased files should be displayed
883  // find which project owns the blockfile
884  // note: there may be more than 1, but just go with the first one.
885  //size_t numProjects = gAudacityProjects.size();
886  AProjectHolder offendingProject;
887  wxString missingFileName;
888 
889  {
891  offendingProject = m_LastMissingBlockFileProject.lock();
892  missingFileName = m_LastMissingBlockFilePath;
893  }
894 
895  // if there are no projects open, don't show the warning (user has closed it)
896  if (offendingProject) {
897  offendingProject->Iconize(false);
898  offendingProject->Raise();
899 
900  wxString errorMessage = wxString::Format(_(
901 "One or more external audio files could not be found.\n\
902 It is possible they were moved, deleted, or the drive they \
903 were on was unmounted.\n\
904 Silence is being substituted for the affected audio.\n\
905 The first detected missing file is:\n\
906 %s\n\
907 There may be additional missing files.\n\
908 Choose Help > Diagnostics > Check Dependencies to view a list of \
909 locations of the missing files."), missingFileName);
910 
911  // if an old dialog exists, raise it if it is
912  if (offendingProject->GetMissingAliasFileDialog()) {
913  offendingProject->GetMissingAliasFileDialog()->Raise();
914  } else {
915  ShowAliasMissingDialog(offendingProject.get(), _("Files Missing"),
916  errorMessage, wxT(""), true);
917  }
918  }
919  // Only show this warning once per event (playback/menu item/etc).
921  }
922 }
923 
925 {
927  if (b) {
928  size_t numProjects = gAudacityProjects.size();
929  for (size_t ii = 0; ii < numProjects; ++ii) {
930  // search each project for the blockfile
931  if (gAudacityProjects[ii]->GetDirManager()->ContainsBlockFile(b)) {
933  break;
934  }
935  }
936  }
937  else
939 
940  if (b)
941  m_LastMissingBlockFilePath = b->GetAliasedFileName().GetFullPath();
942  else
943  m_LastMissingBlockFilePath = wxString{};
944 }
945 
947 {
948  // Note that this is can be called by both the main thread and other threads.
949  // I don't believe we need a mutex because we are checking zero vs non-zero,
950  // and the setting from other threads will always be non-zero (true), and the
951  // setting from the main thread is always false.
953  // reset the warnings as they were probably marked by a previous run
956  }
957 }
958 
960 {
962  auto ptr = m_LastMissingBlockFileProject.lock();
963  return ptr && m_aliasMissingWarningShouldShow;
964 }
965 
967 {
968  // Use dynamic_cast so that we get a NULL ptr if we haven't yet
969  // setup our logger.
970  return dynamic_cast<AudacityLogger *>(wxLog::GetActiveTarget());
971 }
972 
973 #if defined(__WXMSW__)
974 #define WL(lang, sublang) (lang), (sublang),
975 #else
976 #define WL(lang,sublang)
977 #endif
978 
979 #if wxCHECK_VERSION(3, 0, 1)
980 wxLanguageInfo userLangs[] =
981 {
982  // Bosnian is defined in wxWidgets already
983 // { wxLANGUAGE_USER_DEFINED, wxT("bs"), WL(0, SUBLANG_DEFAULT) wxT("Bosnian"), wxLayout_LeftToRight },
984 
985  { wxLANGUAGE_USER_DEFINED, wxT("eu"), WL(0, SUBLANG_DEFAULT) wxT("Basque"), wxLayout_LeftToRight },
986 };
987 #endif
988 
989 wxString AudacityApp::InitLang( const wxString & lang )
990 {
991  wxString result = lang;
992 
993  mLocale.reset();
994 
995 #if defined(__WXMAC__)
996  // This should be reviewed again during the wx3 conversion.
997 
998  // On OSX, if the LANG environment variable isn't set when
999  // using a language like Japanese, an assertion will trigger
1000  // because conversion to Japanese from "?" doesn't return a
1001  // valid length, so make OSX happy by defining/overriding
1002  // the LANG environment variable with U.S. English for now.
1003  wxSetEnv(wxT("LANG"), wxT("en_US.UTF-8"));
1004 #endif
1005 
1006  const wxLanguageInfo *info = NULL;
1007  if (!lang.empty()) {
1008  info = wxLocale::FindLanguageInfo(lang);
1009  if (!info)
1010  ::AudacityMessageBox(wxString::Format(_("Language \"%s\" is unknown"), lang));
1011  }
1012  if (!info)
1013  {
1014  result = GetSystemLanguageCode();
1015  info = wxLocale::FindLanguageInfo(result);
1016  if (!info)
1017  return result;
1018  }
1019  mLocale = std::make_unique<wxLocale>(info->Language);
1020 
1021  for(unsigned int i=0; i<audacityPathList.GetCount(); i++)
1022  mLocale->AddCatalogLookupPathPrefix(audacityPathList[i]);
1023 
1024  // LL: Must add the wxWidgets catalog manually since the search
1025  // paths were not set up when mLocale was created. The
1026  // catalogs are search in LIFO order, so add wxstd first.
1027  mLocale->AddCatalog(wxT("wxstd"));
1028 
1029 // AUDACITY_NAME is legitimately used on some *nix configurations.
1030 #ifdef AUDACITY_NAME
1031  mLocale->AddCatalog(wxT(AUDACITY_NAME));
1032 #else
1033  mLocale->AddCatalog(IPC_APPL);
1034 #endif
1035 
1036  // Initialize internationalisation (number formats etc.)
1037  //
1038  // This must go _after_ creating the wxLocale instance because
1039  // creating the wxLocale instance sets the application-wide locale.
1040 
1041  Internat::Init();
1042 
1043  // Notify listeners of language changes
1044  {
1045  wxCommandEvent evt(EVT_LANGUAGE_CHANGE);
1046  ProcessEvent(evt);
1047  }
1048 
1049  // PRL: Moved this, do it only after language intialized
1050  // Unused strings that we want to be translated, even though
1051  // we're not using them yet...
1052  wxString future1 = _("Master Gain Control");
1053 
1054  return result;
1055 }
1056 
1058 {
1059 #if defined(EXPERIMENTAL_CRASH_REPORT)
1060  GenerateCrashReport(wxDebugReport::Context_Exception);
1061 #endif
1062 
1063  exit(-1);
1064 }
1065 
1066 
1067 #ifdef _MSC_VER
1068 // If this is compiled with MSVC (Visual Studio)
1069 #pragma warning( push )
1070 #pragma warning( disable : 4702) // unreachable code warning.
1071 #endif //_MSC_VER
1072 
1074 {
1075  // This function is invoked from catch blocks in the wxWidgets framework,
1076  // and throw; without argument re-throws the exception being handled,
1077  // letting us dispatch according to its type.
1078 
1079  try { throw; }
1080  catch ( AudacityException &e ) {
1081  // Here is the catch-all for our own exceptions
1082 
1083  // Use CallAfter to delay this to the next pass of the event loop,
1084  // rather than risk doing it inside stack unwinding.
1085  auto pProject = ::GetActiveProject();
1086  auto pException = std::current_exception();
1087  CallAfter( [=] // Capture pException by value!
1088  {
1089 
1090  // Restore the state of the project to what it was before the
1091  // failed operation
1092  pProject->RollbackState();
1093 
1094  // Forget pending changes in the TrackList
1095  pProject->GetTracks()->ClearPendingTracks();
1096 
1097  pProject->RedrawProject();
1098 
1099  // Give the user an alert
1100  try { std::rethrow_exception( pException ); }
1101  catch( AudacityException &e )
1102  { e.DelayedHandlerAction(); }
1103 
1104  } );
1105 
1106  // Don't quit the program
1107  return true;
1108  }
1109  catch ( ... ) {
1110  // There was some other type of exception we don't know.
1111  // Let the inherited function do throw; again and whatever else it does.
1112  return wxApp::OnExceptionInMainLoop();
1113  }
1114  // Shouldn't ever reach this line
1115  return false;
1116 }
1117 #ifdef _MSC_VER
1118 #pragma warning( pop )
1119 #endif //_MSC_VER
1120 
1121 #if defined(EXPERIMENTAL_CRASH_REPORT)
1122 void AudacityApp::GenerateCrashReport(wxDebugReport::Context ctx)
1123 {
1124  wxDebugReportCompress rpt;
1125  rpt.AddAll(ctx);
1126 
1127  wxFileName fn(FileNames::DataDir(), wxT("audacity.cfg"));
1128  rpt.AddFile(fn.GetFullPath(), _TS("Audacity Configuration"));
1129  rpt.AddFile(FileNames::PluginRegistry(), wxT("Plugin Registry"));
1130  rpt.AddFile(FileNames::PluginSettings(), wxT("Plugin Settings"));
1131 
1132  if (ctx == wxDebugReport::Context_Current)
1133  {
1134  rpt.AddText(wxT("audiodev.txt"), gAudioIO->GetDeviceInfo(), wxT("Audio Device Info"));
1135 #ifdef EXPERIMENTAL_MIDI_OUT
1136  rpt.AddText(wxT("mididev.txt"), gAudioIO->GetMidiDeviceInfo(), wxT("MIDI Device Info"));
1137 #endif
1138  }
1139 
1140  AudacityLogger *logger = GetLogger();
1141  if (logger)
1142  {
1143  rpt.AddText(wxT("log.txt"), logger->GetLog(), _TS("Audacity Log"));
1144  }
1145 
1146  bool ok = wxDebugReportPreviewStd().Show(rpt);
1147 
1148 #if defined(__WXMSW__)
1149  wxEventLoop::SetCriticalWindow(NULL);
1150 #endif
1151 
1152  if (ok && rpt.Process())
1153  {
1154  AudacityTextEntryDialog dlg(NULL,
1155  _("Report generated to:"),
1156  _("Audacity Support Data"),
1157  rpt.GetCompressedFileName(),
1158  wxOK | wxCENTER);
1159  dlg.SetName(dlg.GetTitle());
1160  dlg.ShowModal();
1161 
1162  wxLogMessage(wxT("Report generated to: %s"),
1163  rpt.GetCompressedFileName());
1164 
1165  rpt.Reset();
1166  }
1167 }
1168 #endif
1169 
1170 int AudacityApp::FilterEvent(wxEvent & event)
1171 {
1172  (void)event;// compiler food (stops unused parameter warning)
1173 #if !wxCHECK_VERSION(3, 0, 0) && defined(__WXGTK__)
1174  // On wxGTK, there's a focus issue where dialogs do not automatically pass focus
1175  // to the first child. This means that you can use the keyboard to navigate within
1176  // the dialog. Watching for the ACTIVATE event allows us to set the focus ourselves
1177  // when each dialog opens.
1178  //
1179  // See bug #57
1180  //
1181  if (event.GetEventType() == wxEVT_ACTIVATE)
1182  {
1183  wxActivateEvent & e = (wxActivateEvent &) event;
1184 
1185  if (e.GetEventObject() && e.GetActive() && e.GetEventObject()->IsKindOf(CLASSINFO(wxDialog)))
1186  {
1187  ((wxWindow *)e.GetEventObject())->SetFocus();
1188  }
1189  }
1190 #endif
1191 
1192 
1193 #if defined(__WXMAC__) || defined(__WXGTK__)
1194  if (event.GetEventType() == wxEVT_ACTIVATE)
1195  {
1196  wxActivateEvent & e = static_cast<wxActivateEvent &>(event);
1197 
1198  const auto object = e.GetEventObject();
1199  if (object && e.GetActive() &&
1200  object->IsKindOf(CLASSINFO(wxWindow)))
1201  {
1202  const auto window = ((wxWindow *)e.GetEventObject());
1203  window->SetFocus();
1204 #if defined(__WXMAC__)
1205  window->NavigateIn();
1206 #endif
1207  }
1208  }
1209 #endif
1210 
1211  return Event_Skip;
1212 }
1213 
1215 {
1216 // Do not capture crashes in debug builds
1217 #if !defined(__WXDEBUG__)
1218 #if defined(EXPERIMENTAL_CRASH_REPORT)
1219 #if defined(wxUSE_ON_FATAL_EXCEPTION) && wxUSE_ON_FATAL_EXCEPTION
1220  wxHandleFatalExceptions();
1221 #endif
1222 #endif
1223 #endif
1224 }
1225 
1227 {
1228 }
1229 
1230 // The `main program' equivalent, creating the windows and returning the
1231 // main frame
1233 {
1234  // JKC: ANSWER-ME: Who actually added the event loop guarantor?
1235  // Although 'blame' says Leland, I think it came from a donated patch.
1236 
1237  // PRL: It was added by LL at 54676a72285ba7ee3a69920e91fa390a71ef10c9 :
1238  // " Ensure OnInit() has an event loop
1239  // And allow events to flow so the splash window updates under GTK"
1240  // then mistakenly lost in the merge at
1241  // 37168ebbf67ae869ab71a3b5cbbf1d2a48e824aa
1242  // then restored at 7687972aa4b2199f0717165235f3ef68ade71e08
1243 
1244  // Ensure we have an event loop during initialization
1245  wxEventLoopGuarantor eventLoop;
1246 
1247  // wxWidgets will clean up the logger for the main thread, so we can say
1248  // safenew. See:
1249  // http://docs.wxwidgets.org/3.0/classwx_log.html#a2525bf54fa3f31dc50e6e3cd8651e71d
1250  std::unique_ptr < wxLog >
1251  { wxLog::SetActiveTarget(safenew AudacityLogger) }; // DELETE old
1252 
1253  mLocale = NULL;
1254 
1256 
1257 #if defined(__WXMAC__)
1258  // Disable window animation
1259  wxSystemOptions::SetOption(wxMAC_WINDOW_PLAIN_TRANSITION, 1);
1260 #endif
1261 
1262  // Don't use AUDACITY_NAME here.
1263  // We want Audacity with a capital 'A'
1264 
1265 // DA: App name
1266 #ifndef EXPERIMENTAL_DA
1267  wxString appName = wxT("Audacity");
1268 #else
1269  wxString appName = wxT("DarkAudacity");
1270 #endif
1271 
1272  wxTheApp->SetAppName(appName);
1273  // Explicitly set since OSX will use it for the "Quit" menu item
1274  wxTheApp->SetAppDisplayName(appName);
1275  wxTheApp->SetVendorName(appName);
1276 
1277  ::wxInitAllImageHandlers();
1278 
1279  // AddHandler takes ownership
1280  wxFileSystem::AddHandler(safenew wxZipFSHandler);
1281 
1282  //
1283  // Paths: set search path and temp dir path
1284  //
1285 
1286 #ifdef __WXGTK__
1287  /* Search path (for plug-ins, translations etc) is (in this order):
1288  * The AUDACITY_PATH environment variable
1289  * The current directory
1290  * The user's .audacity-files directory in their home directory
1291  * The "share" and "share/doc" directories in their install path */
1292  wxString home = wxGetHomeDir();
1293 
1294  /* On Unix systems, the default temp dir is in /var/tmp. */
1295  defaultTempDir.Printf(wxT("/var/tmp/audacity-%s"), wxGetUserId());
1296 
1297 // DA: Path env variable.
1298 #ifndef EXPERIMENTAL_DA
1299  wxString pathVar = wxGetenv(wxT("AUDACITY_PATH"));
1300 #else
1301  wxString pathVar = wxGetenv(wxT("DARKAUDACITY_PATH"));
1302 #endif
1303  if (pathVar != wxT(""))
1306 
1307 #ifdef AUDACITY_NAME
1308  AddUniquePathToPathList(wxString::Format(wxT("%s/.%s-files"),
1309  home, wxT(AUDACITY_NAME)),
1311  AddUniquePathToPathList(wxString::Format(wxT("%s/share/%s"),
1312  wxT(INSTALL_PREFIX), wxT(AUDACITY_NAME)),
1314  AddUniquePathToPathList(wxString::Format(wxT("%s/share/doc/%s"),
1315  wxT(INSTALL_PREFIX), wxT(AUDACITY_NAME)),
1317 #else //AUDACITY_NAME
1318  AddUniquePathToPathList(wxString::Format(wxT("%s/.audacity-files"),
1319  home),
1321  AddUniquePathToPathList(wxString::Format(wxT("%s/share/audacity"),
1322  wxT(INSTALL_PREFIX)),
1324  AddUniquePathToPathList(wxString::Format(wxT("%s/share/doc/audacity"),
1325  wxT(INSTALL_PREFIX)),
1327 #endif //AUDACITY_NAME
1328 
1329  AddUniquePathToPathList(wxString::Format(wxT("%s/share/locale"),
1330  wxT(INSTALL_PREFIX)),
1332 
1333  AddUniquePathToPathList(wxString::Format(wxT("./locale")),
1335 
1336 #endif //__WXGTK__
1337 
1338 // JKC Bug 1220: Use path based on home directory on WXMAC
1339 #ifdef __WXMAC__
1340  wxFileName tmpFile;
1341  tmpFile.AssignHomeDir();
1342  wxString tmpDirLoc = tmpFile.GetPath(wxPATH_GET_VOLUME);
1343 #else
1344  wxFileName tmpFile;
1345  tmpFile.AssignTempFileName(wxT("nn"));
1346  wxString tmpDirLoc = tmpFile.GetPath(wxPATH_GET_VOLUME);
1347  ::wxRemoveFile(tmpFile.GetFullPath());
1348 #endif
1349 
1350 
1351 
1352  // On Mac and Windows systems, use the directory which contains Audacity.
1353 #ifdef __WXMSW__
1354  // On Windows, the path to the Audacity program is in argv[0]
1355  wxString progPath = wxPathOnly(argv[0]);
1357  AddUniquePathToPathList(progPath + wxT("\\Languages"), audacityPathList);
1358 
1359  // See bug #1271 for explanation of location
1360  tmpDirLoc = FileNames::MkDir(wxStandardPaths::Get().GetUserLocalDataDir());
1361  defaultTempDir.Printf(wxT("%s\\SessionData"),
1362  tmpDirLoc);
1363 #endif //__WXWSW__
1364 
1365 #ifdef __WXMAC__
1366  // On Mac OS X, the path to the Audacity program is in argv[0]
1367  wxString progPath = wxPathOnly(argv[0]);
1368 
1370  // If Audacity is a "bundle" package, then the root directory is
1371  // the great-great-grandparent of the directory containing the executable.
1372  //AddUniquePathToPathList(progPath + wxT("/../../../"), audacityPathList);
1373 
1374  // These allow for searching the "bundle"
1375  AddUniquePathToPathList(progPath + wxT("/../"), audacityPathList);
1376  AddUniquePathToPathList(progPath + wxT("/../Resources"), audacityPathList);
1377 
1378  // JKC Bug 1220: Using an actual temp directory for session data on Mac was
1379  // wrong because it would get cleared out on a reboot.
1380  defaultTempDir.Printf(wxT("%s/Library/Application Support/audacity/SessionData"),
1381  tmpDirLoc);
1382 
1383  //defaultTempDir.Printf(wxT("%s/audacity-%s"),
1384  // tmpDirLoc,
1385  // wxGetUserId());
1386 #endif //__WXMAC__
1387 
1388  // Define languanges for which we have translations, but that are not yet
1389  // supported by wxWidgets.
1390  //
1391  // TODO: The whole Language initialization really need to be reworked.
1392  // It's all over the place.
1393 #if wxCHECK_VERSION(3, 0, 1)
1394  for (size_t i = 0, cnt = WXSIZEOF(userLangs); i < cnt; i++)
1395  {
1396  wxLocale::AddLanguage(userLangs[i]);
1397  }
1398 #endif
1399 
1400  // Initialize preferences and language
1401  InitPreferences();
1402 
1403 #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
1404  this->AssociateFileTypes();
1405 #endif
1406 
1407  // TODO - read the number of files to store in history from preferences
1408  mRecentFiles = std::make_unique<FileHistory>(ID_RECENT_LAST - ID_RECENT_FIRST + 1, ID_RECENT_CLEAR);
1409  mRecentFiles->Load(*gPrefs, wxT("RecentFiles"));
1410 
1412 
1413  // AColor depends on theTheme.
1414  AColor::Init();
1415 
1416  // Init DirManager, which initializes the temp directory
1417  // If this fails, we must exit the program.
1418  if (!InitTempDir()) {
1420  return false;
1421  }
1422 
1423  //<<<< Try to avoid dialogs before this point.
1424  // The reason is that InitTempDir starts the single instance checker.
1425  // If we're waiitng in a dialog before then we can very easily
1426  // start multiple instances, defeating the single instance checker.
1427 
1428  // Initialize the CommandHandler
1430 
1431  // Initialize the PluginManager
1433 
1434  // Initialize the ModuleManager, including loading found modules
1436 
1437  // Parse command line and handle options that might require
1438  // immediate exit...no need to initialize all of the audio
1439  // stuff to display the version string.
1440  std::shared_ptr< wxCmdLineParser > parser{ ParseCommandLine().release() };
1441  if (!parser)
1442  {
1443  // Either user requested help or a parsing error occured
1444  exit(1);
1445  }
1446 
1447  if (parser->Found(wxT("v")))
1448  {
1449  wxFprintf(stderr, wxT("Audacity v%s\n"), AUDACITY_VERSION_STRING);
1450  exit(0);
1451  }
1452 
1453  long lval;
1454  if (parser->Found(wxT("b"), &lval))
1455  {
1456  if (lval < 256 || lval > 100000000)
1457  {
1458  wxPrintf(_("Block size must be within 256 to 100000000\n"));
1459  exit(1);
1460  }
1461 
1463  }
1464 
1465  wxString fileName;
1466  if (parser->Found(wxT("d"), &fileName))
1467  {
1468  AutoSaveFile asf;
1469  if (asf.Decode(fileName))
1470  {
1471  wxPrintf(_("File decoded successfully\n"));
1472  }
1473  else
1474  {
1475  wxPrintf(_("Decoding failed\n"));
1476  }
1477  exit(1);
1478  }
1479 
1480  // BG: Create a temporary window to set as the top window
1481  wxImage logoimage((const char **)AudacityLogoWithName_xpm);
1482  logoimage.Rescale(logoimage.GetWidth() / 2, logoimage.GetHeight() / 2);
1483  wxBitmap logo(logoimage);
1484 
1485  AudacityProject *project;
1486  {
1487  // Bug 718: Position splash screen on same screen
1488  // as where Audacity project will appear.
1489  wxRect wndRect;
1490  bool bMaximized = false;
1491  bool bIconized = false;
1492  GetNextWindowPlacement(&wndRect, &bMaximized, &bIconized);
1493 
1494  wxSplashScreen temporarywindow(
1495  logo,
1496  wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_NO_TIMEOUT,
1497  0,
1498  NULL,
1499  wxID_ANY,
1500  wndRect.GetTopLeft(),
1501  wxDefaultSize,
1502  wxSTAY_ON_TOP);
1503 
1504  // Unfortunately with the Windows 10 Creators update, the splash screen
1505  // now appears before setting its position.
1506  // On a dual monitor screen it will appear on one screen and then
1507  // possibly jump to the second.
1508  // We could fix this by writing outr own splash screen and using Hide()
1509  // until the splash scren was correctly positioned, then Show()
1510 
1511  // Possibly move it on to the second screen...
1512  temporarywindow.SetPosition( wndRect.GetTopLeft() );
1513  // Centered on whichever screen it is on.
1514  temporarywindow.Center();
1515  temporarywindow.SetTitle(_("Audacity is starting up..."));
1516  SetTopWindow(&temporarywindow);
1517 
1518  // ANSWER-ME: Why is YieldFor needed at all?
1519  //wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI|wxEVT_CATEGORY_USER_INPUT|wxEVT_CATEGORY_UNKNOWN);
1520  wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);
1521 
1522  //JKC: Would like to put module loading here.
1523 
1524  // More initialization
1525 
1526  InitDitherers();
1527  InitAudioIO();
1528 
1529 #ifdef __WXMAC__
1530 
1531  // On the Mac, users don't expect a program to quit when you close the last window.
1532  // Create a menubar that will show when all project windows are closed.
1533 
1534  auto fileMenu = std::make_unique<wxMenu>();
1535  auto urecentMenu = std::make_unique<wxMenu>();
1536  auto recentMenu = urecentMenu.get();
1537  fileMenu->Append(wxID_NEW, wxString(_("&New")) + wxT("\tCtrl+N"));
1538  fileMenu->Append(wxID_OPEN, wxString(_("&Open...")) + wxT("\tCtrl+O"));
1539  fileMenu->AppendSubMenu(urecentMenu.release(), _("Open &Recent..."));
1540  fileMenu->Append(wxID_ABOUT, _("&About Audacity..."));
1541  fileMenu->Append(wxID_PREFERENCES, wxString(_("&Preferences...")) + wxT("\tCtrl+,"));
1542 
1543  {
1544  auto menuBar = std::make_unique<wxMenuBar>();
1545  menuBar->Append(fileMenu.release(), _("&File"));
1546 
1547  // PRL: Are we sure wxWindows will not leak this menuBar?
1548  // The online documentation is not explicit.
1549  wxMenuBar::MacSetCommonMenuBar(menuBar.release());
1550  }
1551 
1552  mRecentFiles->UseMenu(recentMenu);
1553  mRecentFiles->AddFilesToMenu(recentMenu);
1554 
1555  SetExitOnFrameDelete(false);
1556 
1557 #endif //__WXMAC__
1558  temporarywindow.Show(false);
1559  }
1560 
1561  // Workaround Bug 1377 - Crash after Audacity starts and low disk space warning appears
1562  // The temporary splash window is closed AND cleaned up, before attempting to create
1563  // a project and possibly creating a modal warning dialog by doing so.
1564  // Also fixes problem of warning being obscured.
1565  // Downside is that we have no splash screen for the (brief) time that we spend
1566  // creating the project.
1567  // Root cause is problem with wxSplashScreen and other dialogs co-existing, that
1568  // seemed to arrive with wx3.
1569  {
1570  project = CreateNewAudacityProject();
1571  mCmdHandler->SetProject(project);
1572  wxWindow * pWnd = MakeHijackPanel();
1573  if (pWnd)
1574  {
1575  project->Show(false);
1576  pWnd->SetParent(project);
1577  SetTopWindow(pWnd);
1578  pWnd->Show(true);
1579  }
1580  }
1581 
1582  if( project->mShowSplashScreen ){
1583  // This may do a check-for-updates at every start up.
1584  // Mainly this is to tell users of ALPHAS who don't know that they have an ALPHA.
1585  // Disabled for now, after discussion.
1586  // project->MayCheckForUpdates();
1587  project->OnHelpWelcome(*project);
1588  }
1589 
1590  // JKC 10-Sep-2007: Enable monitoring from the start.
1591  // (recommended by lprod.org).
1592  // Monitoring stops again after any
1593  // PLAY or RECORD completes.
1594  // So we also call StartMonitoring when STOP is called.
1595  project->MayStartMonitoring();
1596 
1597  #ifdef USE_FFMPEG
1598  FFmpegStartup();
1599  #endif
1600 
1602 
1603  // Bug1561: delay the recovery dialog, to avoid crashes.
1604  CallAfter( [=] () mutable {
1605  //
1606  // Auto-recovery
1607  //
1608  bool didRecoverAnything = false;
1609  if (!ShowAutoRecoveryDialogIfNeeded(&project, &didRecoverAnything))
1610  {
1611  // Important: Prevent deleting any temporary files!
1613  QuitAudacity(true);
1614  }
1615 
1616  //
1617  // Remainder of command line parsing, but only if we didn't recover
1618  //
1619  if (!didRecoverAnything)
1620  {
1621  if (parser->Found(wxT("t")))
1622  {
1623  RunBenchmark(NULL);
1624  QuitAudacity(true);
1625  }
1626 
1627  // As of wx3, there's no need to process the filename arguments as they
1628  // will be sent view the MacOpenFile() method.
1629 #if !defined(__WXMAC__)
1630  for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++)
1631  {
1632  // PRL: Catch any exceptions, don't try this file again, continue to
1633  // other files.
1634  SafeMRUOpen(parser->GetParam(i));
1635  }
1636 #endif
1637  }
1638  } );
1639 
1640  gInited = true;
1641 
1643 
1644  mWindowRectAlreadySaved = FALSE;
1645 
1646  mTimer.SetOwner(this, kAudacityAppTimerID);
1647  mTimer.Start(200);
1648 
1649  return TRUE;
1650 }
1651 
1653 {
1654  mCmdHandler = std::make_unique<CommandHandler>();
1655  //SetNextHandler(mCmdHandler);
1656 }
1657 
1658 // AppCommandEvent callback - just pass the event on to the CommandHandler
1660 {
1661  wxASSERT(NULL != mCmdHandler);
1662  mCmdHandler->OnReceiveCommand(event);
1663 }
1664 
1665 void AudacityApp::OnKeyDown(wxKeyEvent &event)
1666 {
1667  if(event.GetKeyCode() == WXK_ESCAPE) {
1668  // Stop play, including scrub, but not record
1669  auto project = ::GetActiveProject();
1670  auto token = project->GetAudioIOToken();
1671  auto &scrubber = project->GetScrubber();
1672  auto scrubbing = scrubber.HasStartedScrubbing();
1673  if (scrubbing)
1674  scrubber.Cancel();
1675  if((token > 0 &&
1676  gAudioIO->IsAudioTokenActive(token) &&
1677  gAudioIO->GetNumCaptureChannels() == 0) ||
1678  scrubbing)
1679  // ESC out of other play (but not record)
1680  project->OnStop(*project);
1681  else
1682  event.Skip();
1683  }
1684  else
1685  event.Skip();
1686 }
1687 
1688 // We now disallow temp directory name that puts it where cleaner apps will
1689 // try to clean out the files.
1690 bool AudacityApp::IsTempDirectoryNameOK( const wxString & Name ){
1691  if( Name.IsEmpty() )
1692  return false;
1693 
1694  wxFileName tmpFile;
1695  tmpFile.AssignTempFileName(wxT("nn"));
1696  // use Long Path to expand out any abbreviated long substrings.
1697  wxString BadPath = tmpFile.GetLongPath();
1698  ::wxRemoveFile(tmpFile.GetFullPath());
1699 
1700 #ifdef __WXMAC__
1701  // This test is to fix bug 1220 on a 1.x to 2.x to 2.1.3 upgrade.
1702  // It is less permissive than we could be as it stops a path
1703  // with this string ANYWHERE within it rather than excluding just
1704  // the paths that the earlier Audacities used to create.
1705  if( Name.Contains( "/tmp/") )
1706  return false;
1707  BadPath = BadPath.BeforeLast( '/' ) + "/";
1708  wxFileName cmpFile( Name );
1709  wxString NameCanonical = cmpFile.GetLongPath( ) + "/";
1710 #else
1711  BadPath = BadPath.BeforeLast( '\\' ) + "\\";
1712  wxFileName cmpFile( Name );
1713  wxString NameCanonical = cmpFile.GetLongPath( ) + "\\";
1714 #endif
1715  return !(NameCanonical.StartsWith( BadPath ));
1716 }
1717 
1718 // Ensures directory is created and puts the name into result.
1719 // result is unchanged if unsuccessful.
1720 void SetToExtantDirectory( wxString & result, const wxString & dir ){
1721  // don't allow path of "".
1722  if( dir.IsEmpty() )
1723  return;
1724  if( wxDirExists( dir ) ){
1725  result = dir;
1726  return;
1727  }
1728  // Use '/' so that this works on Mac and Windows alike.
1729  wxFileName name( dir + "/junkname.cfg" );
1730  if( name.Mkdir( wxS_DIR_DEFAULT , wxPATH_MKDIR_FULL ) )
1731  result = dir;
1732 }
1733 
1735 {
1736  // We need to find a temp directory location.
1737 
1738  wxString tempFromPrefs = gPrefs->Read(wxT("/Directories/TempDir"), wxT(""));
1739  wxString tempDefaultLoc = wxGetApp().defaultTempDir;
1740 
1741  wxString temp = wxT("");
1742 
1743  #ifdef __WXGTK__
1744  if (tempFromPrefs.Length() > 0 && tempFromPrefs[0] != wxT('/'))
1745  tempFromPrefs = wxT("");
1746  #endif
1747 
1748  // Stop wxWidgets from printing its own error messages
1749 
1750  wxLogNull logNo;
1751 
1752  // Try temp dir that was stored in prefs first
1753  if( IsTempDirectoryNameOK( tempFromPrefs ) )
1754  SetToExtantDirectory( temp, tempFromPrefs );
1755 
1756  // If that didn't work, try the default location
1757 
1758  if (temp==wxT(""))
1759  SetToExtantDirectory( temp, tempDefaultLoc );
1760 
1761  // Check temp directory ownership on *nix systems only
1762  #ifdef __UNIX__
1763  struct stat tempStatBuf;
1764  if ( lstat(temp.mb_str(), &tempStatBuf) != 0 ) {
1765  temp.clear();
1766  }
1767  else {
1768  if ( geteuid() != tempStatBuf.st_uid ) {
1769  temp.clear();
1770  }
1771  }
1772  #endif
1773 
1774  if (temp == wxT("")) {
1775  // Failed
1776  if( !IsTempDirectoryNameOK( tempFromPrefs ) ) {
1777  AudacityMessageBox(_("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."));
1778  } else {
1779  AudacityMessageBox(_("Audacity could not find a place to store temporary files.\nPlease enter an appropriate directory in the preferences dialog."));
1780  }
1781 
1782  // Only want one page of the preferences
1783  DirectoriesPrefsFactory directoriesPrefsFactory;
1784  PrefsDialog::Factories factories;
1785  factories.push_back(&directoriesPrefsFactory);
1786  GlobalPrefsDialog dialog(NULL, factories);
1787  dialog.ShowModal();
1788 
1789  AudacityMessageBox(_("Audacity is now going to exit. Please launch Audacity again to use the new temporary directory."));
1790  return false;
1791  }
1792 
1793  // The permissions don't always seem to be set on
1794  // some platforms. Hopefully this fixes it...
1795  #ifdef __UNIX__
1796  chmod(OSFILENAME(temp), 0755);
1797  #endif
1798 
1799  bool bSuccess = gPrefs->Write(wxT("/Directories/TempDir"), temp) && gPrefs->Flush();
1800  DirManager::SetTempDir(temp);
1801 
1802  // Make sure the temp dir isn't locked by another process.
1803  if (!CreateSingleInstanceChecker(temp))
1804  return false;
1805 
1806  return bSuccess;
1807 }
1808 
1809 // Return true if there are no other instances of Audacity running,
1810 // false otherwise.
1811 //
1812 // Use "dir" for creating lockfiles (on OS X and Unix).
1813 
1815 {
1816  wxString name = wxString::Format(wxT("audacity-lock-%s"), wxGetUserId());
1817  mChecker.reset();
1818  auto checker = std::make_unique<wxSingleInstanceChecker>();
1819 
1820 #if defined(__UNIX__)
1821  wxString sockFile(dir + wxT("/.audacity.sock"));
1822 #endif
1823 
1824  wxString runningTwoCopiesStr = _("Running two copies of Audacity simultaneously may cause\ndata loss or cause your system to crash.\n\n");
1825 
1826  if (!checker->Create(name, dir)) {
1827  // Error initializing the wxSingleInstanceChecker. We don't know
1828  // whether there is another instance running or not.
1829 
1830  wxString prompt =
1831  _("Audacity was not able to lock the temporary files directory.\nThis folder may be in use by another copy of Audacity.\n") +
1832  runningTwoCopiesStr +
1833  _("Do you still want to start Audacity?");
1834  int action = AudacityMessageBox(prompt,
1835  _("Error Locking Temporary Folder"),
1836  wxYES_NO | wxICON_EXCLAMATION,
1837  NULL);
1838  if (action == wxNO)
1839  return false;
1840  }
1841  else if ( checker->IsAnotherRunning() ) {
1842  // Parse the command line to ensure correct syntax, but
1843  // ignore options and only use the filenames, if any.
1844  auto parser = ParseCommandLine();
1845  if (!parser)
1846  {
1847  // Complaints have already been made
1848  return false;
1849  }
1850 
1851 #if defined(__WXMSW__)
1852  // On Windows, we attempt to make a connection
1853  // to an already active Audacity. If successful, we send
1854  // the first command line argument (the audio file name)
1855  // to that Audacity for processing.
1856  wxClient client;
1857 
1858  // We try up to 50 times since there's a small window
1859  // where the server may not have been fully initialized.
1860  for (int i = 0; i < 50; i++)
1861  {
1862  std::unique_ptr<wxConnectionBase> conn{ client.MakeConnection(wxEmptyString, IPC_APPL, IPC_TOPIC) };
1863  if (conn)
1864  {
1865  bool ok = false;
1866  if (parser->GetParamCount() > 0)
1867  {
1868  // Send each parameter to existing Audacity
1869  for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++)
1870  {
1871  ok = conn->Execute(parser->GetParam(i));
1872  }
1873  }
1874  else
1875  {
1876  // Send an empty string to force existing Audacity to front
1877  ok = conn->Execute(wxEmptyString);
1878  }
1879 
1880  if (ok)
1881  return false;
1882  }
1883 
1884  wxMilliSleep(10);
1885  }
1886 #else
1887  // On Unix-like machines, we use a local (file based) socket to
1888  // send the first command line argument to an already running
1889  // Audacity.
1890  wxUNIXaddress addr;
1891  addr.Filename(sockFile);
1892 
1893  {
1894  // Setup the socket
1895  // A wxSocketClient must not be deleted by us, but rather, let the
1896  // framework do appropriate delayed deletion after Destroy()
1897  Destroy_ptr<wxSocketClient> sock { safenew wxSocketClient() };
1898  sock->SetFlags(wxSOCKET_WAITALL);
1899 
1900  // We try up to 50 times since there's a small window
1901  // where the server may not have been fully initialized.
1902  for (int i = 0; i < 50; i++)
1903  {
1904  // Connect to the existing Audacity
1905  sock->Connect(addr, true);
1906  if (sock->IsConnected())
1907  {
1908  if (parser->GetParamCount() > 0)
1909  {
1910  for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++)
1911  {
1912  // Send the filename
1913  wxString param = parser->GetParam(i);
1914  sock->WriteMsg((const wxChar *) param, (param.Len() + 1) * sizeof(wxChar));
1915  }
1916  }
1917  else
1918  {
1919  // Send an empty string to force existing Audacity to front
1920  sock->WriteMsg(wxEmptyString, sizeof(wxChar));
1921  }
1922 
1923  return sock->Error();
1924  }
1925 
1926  wxMilliSleep(100);
1927  }
1928  }
1929 #endif
1930  // There is another copy of Audacity running. Force quit.
1931 
1932  wxString prompt =
1933  _("The system has detected that another copy of Audacity is running.\n") +
1934  runningTwoCopiesStr +
1935  _("Use the New or Open commands in the currently running Audacity\nprocess to open multiple projects simultaneously.\n");
1936  AudacityMessageBox(prompt, _("Audacity is already running"),
1937  wxOK | wxICON_ERROR);
1938  return false;
1939  }
1940 
1941 #if defined(__WXMSW__)
1942  // Create the DDE IPC server
1943  mIPCServ = std::make_unique<IPCServ>(IPC_APPL);
1944 #else
1945  int mask = umask(077);
1946  remove(OSFILENAME(sockFile));
1947  wxUNIXaddress addr;
1948  addr.Filename(sockFile);
1949  mIPCServ = std::make_unique<wxSocketServer>(addr, wxSOCKET_NOWAIT);
1950  umask(mask);
1951 
1952  if (!mIPCServ || !mIPCServ->IsOk())
1953  {
1954  // TODO: Complain here
1955  return false;
1956  }
1957 
1958  mIPCServ->SetEventHandler(*this, ID_IPC_SERVER);
1959  mIPCServ->SetNotify(wxSOCKET_CONNECTION_FLAG);
1960  mIPCServ->Notify(true);
1961 #endif
1962  mChecker = std::move(checker);
1963  return true;
1964 }
1965 
1966 #if defined(__UNIX__)
1967 void AudacityApp::OnServerEvent(wxSocketEvent & /* evt */)
1968 {
1969  wxSocketBase *sock;
1970 
1971  // Accept all pending connection requests
1972  do
1973  {
1974  sock = mIPCServ->Accept(false);
1975  if (sock)
1976  {
1977  // Setup the socket
1978  sock->SetEventHandler(*this, ID_IPC_SOCKET);
1979  sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
1980  sock->Notify(true);
1981  }
1982  } while (sock);
1983 }
1984 
1985 void AudacityApp::OnSocketEvent(wxSocketEvent & evt)
1986 {
1987  wxSocketBase *sock = evt.GetSocket();
1988 
1989  if (evt.GetSocketEvent() == wxSOCKET_LOST)
1990  {
1991  sock->Destroy();
1992  return;
1993  }
1994 
1995  // Read the length of the filename and bail if we have a short read
1996  wxChar name[PATH_MAX];
1997  sock->ReadMsg(&name, sizeof(name));
1998  if (!sock->Error())
1999  {
2000  // Add the filename to the queue. It will be opened by
2001  // the OnTimer() event when it is safe to do so.
2002  ofqueue.Add(name);
2003  }
2004 }
2005 
2006 #endif
2007 
2008 std::unique_ptr<wxCmdLineParser> AudacityApp::ParseCommandLine()
2009 {
2010  auto parser = std::make_unique<wxCmdLineParser>(argc, argv);
2011  if (!parser)
2012  {
2013  return nullptr;
2014  }
2015 
2016  /*i18n-hint: This controls the number of bytes that Audacity will
2017  * use when writing files to the disk */
2018  parser->AddOption(wxT("b"), wxT("blocksize"), _("set max disk block size in bytes"),
2019  wxCMD_LINE_VAL_NUMBER);
2020 
2021  /*i18n-hint: This decodes an autosave file */
2022  parser->AddOption(wxT("d"), wxT("decode"), _("decode an autosave file"),
2023  wxCMD_LINE_VAL_STRING);
2024 
2025  /*i18n-hint: This displays a list of available options */
2026  parser->AddSwitch(wxT("h"), wxT("help"), _("this help message"),
2027  wxCMD_LINE_OPTION_HELP);
2028 
2029  /*i18n-hint: This runs a set of automatic tests on Audacity itself */
2030  parser->AddSwitch(wxT("t"), wxT("test"), _("run self diagnostics"));
2031 
2032  /*i18n-hint: This displays the Audacity version */
2033  parser->AddSwitch(wxT("v"), wxT("version"), _("display Audacity version"));
2034 
2035  /*i18n-hint: This is a list of one or more files that Audacity
2036  * should open upon startup */
2037  parser->AddParam(_("audio or project file name"),
2038  wxCMD_LINE_VAL_STRING,
2039  wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL);
2040 
2041  // Run the parser
2042  if (parser->Parse() == 0)
2043  return parser;
2044 
2045  return{};
2046 }
2047 
2048 // static
2049 void AudacityApp::AddUniquePathToPathList(const wxString &pathArg,
2050  wxArrayString &pathList)
2051 {
2052  wxFileName pathNorm = pathArg;
2053  pathNorm.Normalize();
2054  const wxString newpath{ pathNorm.GetFullPath() };
2055 
2056  for(unsigned int i=0; i<pathList.GetCount(); i++) {
2057  if (wxFileName(newpath) == wxFileName(pathList[i]))
2058  return;
2059  }
2060 
2061  pathList.Add(newpath);
2062 }
2063 
2064 // static
2065 void AudacityApp::AddMultiPathsToPathList(const wxString &multiPathStringArg,
2066  wxArrayString &pathList)
2067 {
2068  wxString multiPathString(multiPathStringArg);
2069  while (multiPathString != wxT("")) {
2070  wxString onePath = multiPathString.BeforeFirst(wxPATH_SEP[0]);
2071  multiPathString = multiPathString.AfterFirst(wxPATH_SEP[0]);
2072  AddUniquePathToPathList(onePath, pathList);
2073  }
2074 }
2075 
2076 // static
2077 void AudacityApp::FindFilesInPathList(const wxString & pattern,
2078  const wxArrayString & pathList,
2079  wxArrayString & results,
2080  int flags)
2081 {
2082  wxLogNull nolog;
2083 
2084  if (pattern == wxT("")) {
2085  return;
2086  }
2087 
2088  wxFileName ff;
2089 
2090  for(size_t i = 0; i < pathList.GetCount(); i++) {
2091  ff = pathList[i] + wxFILE_SEP_PATH + pattern;
2092  wxDir::GetAllFiles(ff.GetPath(), &results, ff.GetFullName(), flags);
2093  }
2094 }
2095 
2096 void AudacityApp::OnQueryEndSession(wxCloseEvent & event)
2097 {
2098  bool mustVeto = false;
2099 
2100 #ifdef __WXMAC__
2101  mustVeto = wxDialog::OSXHasModalDialogsOpen();
2102 #endif
2103 
2104  if ( mustVeto )
2105  event.Veto(true);
2106  else
2107  OnEndSession(event);
2108 }
2109 
2110 void AudacityApp::OnEndSession(wxCloseEvent & event)
2111 {
2112  bool force = !event.CanVeto();
2113 
2114  // Try to close each open window. If the user hits Cancel
2115  // in a Save Changes dialog, don't continue.
2116  if (!gAudacityProjects.empty()) {
2117  while (gAudacityProjects.size()) {
2118  // Closing the project has side-effect of
2119  // deletion from gAudacityProjects
2120  if (force) {
2121  gAudacityProjects[0]->Close(true);
2122  }
2123  else if (!gAudacityProjects[0]->Close()) {
2124  gIsQuitting = false;
2125  event.Veto();
2126  break;
2127  }
2128  }
2129  }
2130 }
2131 
2132 void AudacityApp::AddFileToHistory(const wxString & name)
2133 {
2134  mRecentFiles->AddFileToHistory(name);
2135 }
2136 
2138 {
2139  gIsQuitting = true;
2140  while(Pending())
2141  {
2142  Dispatch();
2143  }
2144 
2146 
2147  if(gPrefs)
2148  {
2149  bool bFalse = false;
2150  //Should we change the commands.cfg location next startup?
2151  if(gPrefs->Read(wxT("/QDeleteCmdCfgLocation"), &bFalse))
2152  {
2153  gPrefs->DeleteEntry(wxT("/QDeleteCmdCfgLocation"));
2154  gPrefs->Write(wxT("/DeleteCmdCfgLocation"), true);
2155  gPrefs->Flush();
2156  }
2157  }
2158 
2159  mRecentFiles->Save(*gPrefs, wxT("RecentFiles"));
2160 
2162 
2163 #ifdef USE_FFMPEG
2164  DropFFmpegLibs();
2165 #endif
2166 
2167  DeinitFFT();
2168 
2169  DeinitAudioIO();
2170 
2171  // Terminate the PluginManager (must be done before deleting the locale)
2173 
2174  if (mIPCServ)
2175  {
2176 #if defined(__UNIX__)
2177  wxUNIXaddress addr;
2178  if (mIPCServ->GetLocal(addr))
2179  {
2180  remove(OSFILENAME(addr.Filename()));
2181  }
2182 #endif
2183  }
2184 
2185  return 0;
2186 }
2187 
2188 // The following five methods are currently only used on Mac OS,
2189 // where it's possible to have a menu bar but no windows open.
2190 // It doesn't hurt any other platforms, though.
2191 
2192 // ...That is, as long as you check to see if no windows are open
2193 // before executing the stuff.
2194 // To fix this, check to see how many project windows are open,
2195 // and skip the event unless none are open (which should only happen
2196 // on the Mac, at least currently.)
2197 
2198 void AudacityApp::OnMenuAbout(wxCommandEvent & /*event*/)
2199 {
2200  // This function shadows a similar function
2201  // in Menus.cpp, but should only be used on the Mac platform.
2202 #ifdef __WXMAC__
2203  // Modeless dialog, consistent with other Mac applications
2204  // Not more than one at once!
2205  const auto instance = AboutDialog::ActiveIntance();
2206  if (instance)
2207  instance->Raise();
2208  else
2209  // This dialog deletes itself when dismissed
2210  (safenew AboutDialog{ nullptr })->Show(true);
2211 #else
2212  wxASSERT(false);
2213 #endif
2214 }
2215 
2216 void AudacityApp::OnMenuNew(wxCommandEvent & event)
2217 {
2218  // This function shadows a similar function
2219  // in Menus.cpp, but should only be used on the Mac platform
2220  // when no project windows are open. This check assures that
2221  // this happens, and enable the same code to be present on
2222  // all platforms.
2223 
2224  if(gAudacityProjects.size() == 0)
2226  else
2227  event.Skip();
2228 }
2229 
2230 
2231 void AudacityApp::OnMenuOpen(wxCommandEvent & event)
2232 {
2233  // This function shadows a similar function
2234  // in Menus.cpp, but should only be used on the Mac platform
2235  // when no project windows are open. This check assures that
2236  // this happens, and enable the same code to be present on
2237  // all platforms.
2238 
2239 
2240  if(gAudacityProjects.size() == 0)
2242  else
2243  event.Skip();
2244 
2245 
2246 }
2247 
2248 void AudacityApp::OnMenuPreferences(wxCommandEvent & event)
2249 {
2250  // This function shadows a similar function
2251  // in Menus.cpp, but should only be used on the Mac platform
2252  // when no project windows are open. This check assures that
2253  // this happens, and enable the same code to be present on
2254  // all platforms.
2255 
2256  if(gAudacityProjects.size() == 0) {
2257  GlobalPrefsDialog dialog(NULL /* parent */ );
2258  dialog.ShowModal();
2259  }
2260  else
2261  event.Skip();
2262 
2263 }
2264 
2265 void AudacityApp::OnMenuExit(wxCommandEvent & event)
2266 {
2267  // This function shadows a similar function
2268  // in Menus.cpp, but should only be used on the Mac platform
2269  // when no project windows are open. This check assures that
2270  // this happens, and enable the same code to be present on
2271  // all platforms.
2272 
2273  // LL: Removed "if" to allow closing based on final project count.
2274  // if(gAudacityProjects.size() == 0)
2275  QuitAudacity();
2276 
2277  // LL: Veto quit if projects are still open. This can happen
2278  // if the user selected Cancel in a Save dialog.
2279  event.Skip(gAudacityProjects.size() == 0);
2280 
2281 }
2282 
2283 //BG: On Windows, associate the aup file type with Audacity
2284 /* We do this in the Windows installer now,
2285  to avoid issues where user doesn't have admin privileges, but
2286  in case that didn't work, allow the user to decide at startup.
2287 
2288  //v Should encapsulate this & allow access from Prefs, too,
2289  // if people want to manually change associations.
2290 */
2291 #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
2292 void AudacityApp::AssociateFileTypes()
2293 {
2294  wxRegKey associateFileTypes;
2295  associateFileTypes.SetName(wxT("HKCR\\.AUP"));
2296  bool bKeyExists = associateFileTypes.Exists();
2297  if (!bKeyExists) {
2298  // Not at HKEY_CLASSES_ROOT. Try HKEY_CURRENT_USER.
2299  associateFileTypes.SetName(wxT("HKCU\\Software\\Classes\\.AUP"));
2300  bKeyExists = associateFileTypes.Exists();
2301  }
2302  if (!bKeyExists) {
2303  // File types are not currently associated.
2304  // Check pref in case user has already decided against it.
2305  bool bWantAssociateFiles = true;
2306  if (!gPrefs->Read(wxT("/WantAssociateFiles"), &bWantAssociateFiles) ||
2307  bWantAssociateFiles) {
2308  // Either there's no pref or user does want associations
2309  // and they got stepped on, so ask.
2310  int wantAssoc =
2312  _("Audacity project (.AUP) files are not currently \nassociated with Audacity. \n\nAssociate them, so they open on double-click?"),
2313  _("Audacity Project Files"),
2314  wxYES_NO | wxICON_QUESTION);
2315  if (wantAssoc == wxYES) {
2316  gPrefs->Write(wxT("/WantAssociateFiles"), true);
2317  gPrefs->Flush();
2318 
2319  wxString root_key;
2320 
2321  root_key = wxT("HKCU\\Software\\Classes\\");
2322  associateFileTypes.SetName(root_key + wxT(".AUP")); // Start again with HKEY_CLASSES_ROOT.
2323  if (!associateFileTypes.Create(true)) {
2324  // Not at HKEY_CLASSES_USER. Try HKEY_CURRENT_ROOT.
2325  root_key = wxT("HKCR\\");
2326  associateFileTypes.SetName(root_key + wxT(".AUP"));
2327  if (!associateFileTypes.Create(true)) {
2328  // Actually, can't create keys. Empty root_key to flag failure.
2329  root_key.Empty();
2330  }
2331  }
2332  if (root_key.IsEmpty()) {
2333  //v Warn that we can't set keys. Ask whether to set pref for no retry?
2334  } else {
2335  associateFileTypes = wxT("Audacity.Project"); // Finally set value for .AUP key
2336 
2337  associateFileTypes.SetName(root_key + wxT("Audacity.Project"));
2338  if(!associateFileTypes.Exists()) {
2339  associateFileTypes.Create(true);
2340  associateFileTypes = wxT("Audacity Project File");
2341  }
2342 
2343  associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell"));
2344  if(!associateFileTypes.Exists()) {
2345  associateFileTypes.Create(true);
2346  associateFileTypes = wxT("");
2347  }
2348 
2349  associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open"));
2350  if(!associateFileTypes.Exists()) {
2351  associateFileTypes.Create(true);
2352  }
2353 
2354  associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\command"));
2355  wxString tmpRegAudPath;
2356  if(associateFileTypes.Exists()) {
2357  tmpRegAudPath = wxString(associateFileTypes).Lower();
2358  }
2359  if (!associateFileTypes.Exists() ||
2360  (tmpRegAudPath.Find(wxT("audacity.exe")) >= 0)) {
2361  associateFileTypes.Create(true);
2362  associateFileTypes = (wxString)argv[0] + (wxString)wxT(" \"%1\"");
2363  }
2364 
2365 #if 0
2366  // These can be use later to support more startup messages
2367  // like maybe "Import into existing project" or some such.
2368  // Leaving here for an example...
2369  associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec"));
2370  if(!associateFileTypes.Exists()) {
2371  associateFileTypes.Create(true);
2372  associateFileTypes = wxT("%1");
2373  }
2374 
2375  associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Application"));
2376  if(!associateFileTypes.Exists()) {
2377  associateFileTypes.Create(true);
2378  associateFileTypes = IPC_APPL;
2379  }
2380 
2381  associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Topic"));
2382  if(!associateFileTypes.Exists()) {
2383  associateFileTypes.Create(true);
2384  associateFileTypes = IPC_TOPIC;
2385  }
2386 #endif
2387  }
2388  } else {
2389  // User said no. Set a pref so we don't keep asking.
2390  gPrefs->Write(wxT("/WantAssociateFiles"), false);
2391  gPrefs->Flush();
2392  }
2393  }
2394  }
2395 }
2396 #endif
2397 
void FinishPreferences()
Definition: Prefs.cpp:369
static AboutDialog * ActiveIntance()
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
#define AUDACITY_VERSION_STRING
Definition: Audacity.h:81
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:209
#define ID_IPC_SOCKET
void OnReceiveCommand(AppCommandEvent &event)
wxTimer mTimer
Definition: AudacityApp.h:186
void GetNextWindowPlacement(wxRect *nextRect, bool *pMaximized, bool *pIconized)
Definition: Project.cpp:718
void OnQueryEndSession(wxCloseEvent &event)
bool GetDirty()
Definition: Project.h:303
root of a hierarchy of classes that are thrown and caught by Audacity.
bool mWindowRectAlreadySaved
Definition: AudacityApp.h:201
static ModuleManager & Get()
static void Quit()
Kills the ODMananger Thread.
Definition: ODManager.cpp:363
std::unique_ptr< wxSocketServer > mIPCServ
Definition: AudacityApp.h:206
void SetMissingAliasedFileWarningShouldShow(bool b)
Changes the behavior of missing aliased blockfiles warnings.
std::weak_ptr< AudacityProject > m_LastMissingBlockFileProject
Definition: AudacityApp.h:189
void SaveWindowSize()
AudacityLogger * GetLogger()
static bool IsAlreadyOpen(const wxString &projPathName)
Definition: Project.cpp:2860
AProjectArray gAudacityProjects
Definition: Project.cpp:303
void OnEndSession(wxCloseEvent &event)
bool m_aliasMissingWarningShouldShow
Definition: AudacityApp.h:188
DEFINE_EVENT_TYPE(EVT_OPEN_AUDIO_FILE)
Custom events.
void RunBenchmark(wxWindow *parent)
Definition: Benchmark.cpp:82
ODLock m_LastMissingBlockFileLock
Definition: AudacityApp.h:192
unsigned GetNumCaptureChannels() const
Definition: AudioIO.h:377
#define _TS(s)
Definition: Internat.h:30
bool mShowSplashScreen
Definition: Project.h:705
bool OnExceptionInMainLoop() override
bool Initialize()
Definition: Import.cpp:84
static void Init()
Initialize internationalisation support. Call this once at program start.
Definition: Internat.cpp:82
void OnMenuNew(wxCommandEvent &event)
void OnMenuOpen(wxCommandEvent &event)
void MayStartMonitoring()
Definition: Project.cpp:5527
virtual void DelayedHandlerAction()=0
bool IsAudioTokenActive(int token)
Returns true if the stream is active, or even if audio I/O is busy cleaning up its data or writing to...
Definition: AudioIO.cpp:2937
void OnMRUFile(wxCommandEvent &event)
void OnMenuAbout(wxCommandEvent &event)
#define ID_RECENT_FIRST
int AudacityMessageBox(const wxString &message, const wxString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: ErrorDialog.h:92
#define OSFILENAME(X)
Definition: Internat.h:173
void OnMenuExit(wxCommandEvent &event)
void InitCommandHandler()
Headers and event table macros for AppCommandEvent.
void EnsureInitialised() override
Definition: Theme.cpp:221
static void AddMultiPathsToPathList(const wxString &multiPathString, wxArrayString &pathList)
std::unique_ptr< CommandHandler > mCmdHandler
Definition: AudacityApp.h:179
static wxString PluginSettings()
Definition: FileNames.cpp:224
#define EVT_APP_COMMAND(winid, fn)
Wrap wxTextEntryDialog so that caption IS translatable.
Definition: ErrorDialog.h:108
static void UpdateDefaultPath(Operation op, const wxString &path)
Definition: FileNames.cpp:399
std::shared_ptr< AudacityProject > AProjectHolder
Definition: Project.h:119
static void FindFilesInPathList(const wxString &pattern, const wxArrayString &pathList, wxArrayString &results, int flags=wxDIR_FILES)
AudacityLogger is a thread-safe logger class.
void DeinitAudioIO()
Definition: AudioIO.cpp:1149
#define safenew
Definition: Audacity.h:230
int Dispatch(ModuleDispatchTypes type)
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:417
bool ShouldShowMissingAliasedFileWarning()
Returns true if the user should be notified of missing alias warnings.
void AddFileToHistory(const wxString &name)
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:176
int OnExit(void) override
void SetWindowRectAlreadySaved(bool alreadySaved)
Definition: AudacityApp.h:157
const wxFileName & GetAliasedFileName() const
Definition: BlockFile.h:278
wxString InitLang(const wxString &lang)
void InitDitherers()
bool GetIsEmpty()
Definition: Project.cpp:1500
static bool IsTempDirectoryNameOK(const wxString &Name)
#define WL(lang, sublang)
The AboutDialog shows the program version and developer credits.
Definition: AboutDialog.h:51
#define IPC_APPL
static void SetMaxDiskBlockSize(size_t bytes)
Definition: Sequence.cpp:2011
void OnFatalException() override
#define kAudacityAppTimerID
bool OnExec(const wxString &WXUNUSED(topic), const wxString &data)
std::vector< PrefsNode > Factories
Definition: PrefsDialog.h:50
wxString m_LastMissingBlockFilePath
Definition: AudacityApp.h:190
bool SafeMRUOpen(const wxString &fileName)
#define IPC_TOPIC
static void Init()
Definition: AColor.cpp:460
bool CreateSingleInstanceChecker(const wxString &dir)
static wxArrayString ofqueue
void QuitAudacity(bool bForce)
AudacityApp is the 'main' class for Audacity.
Definition: AudacityApp.h:59
IPCServ(const wxString &appl)
void ShowAliasMissingDialog(AudacityProject *parent, const wxString &dlogTitle, const wxString &message, const wxString &helpPage, const bool Close)
Displays a custom modeless error dialog for aliased file errors.
void OnServerEvent(wxSocketEvent &evt)
void OnMenuPreferences(wxCommandEvent &event)
void OnSocketEvent(wxSocketEvent &evt)
void GetDefaultWindowRect(wxRect *defRect)
Definition: Project.cpp:623
void InitPreferences()
Definition: Prefs.cpp:169
bool OnInit(void) override
static bool gInited
#define ID_IPC_SERVER
wxConnectionBase * OnAcceptConnection(const wxString &topic) override
void OnKeyDown(wxKeyEvent &event)
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
static void AddUniquePathToPathList(const wxString &path, wxArrayString &pathList)
wxString GetSystemLanguageCode()
Definition: Languages.cpp:76
AudioIO * gAudioIO
Definition: AudioIO.cpp:482
Contains declarations for the CommandHandler class.
An event 'envelope' for sending Command objects through the wxwidgets event loop. ...
void CloseScreenshotTools()
Definition: Screenshot.cpp:128
int ShowModal() override
const wxChar * name
Definition: Distortion.cpp:94
bool InitTempDir()
static void OpenFiles(AudacityProject *proj)
Definition: Project.cpp:2880
wxDEFINE_EVENT(EVT_LANGUAGE_CHANGE, wxCommandEvent)
void MarkAliasedFilesMissingWarning(const AliasBlockFile *b)
Mark playback as having missing aliased blockfiles.
static wxString PluginRegistry()
Definition: FileNames.cpp:219
bool Decode(const wxString &fileName)
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:308
A BlockFile that refers to data in an existing file.
Definition: BlockFile.h:251
bool MRUOpen(const wxString &fileName)
void Initialize(CommandHandler &cmdHandler)
wxArrayString audacityPathList
A list of directories that should be searched for Audacity files (plug-ins, help files, etc.).
Definition: AudacityApp.h:138
#define ID_RECENT_LAST
static wxString DataDir()
Audacity user data directory.
Definition: FileNames.cpp:130
void OnTimer(wxTimerEvent &event)
void CloseScoreAlignDialog()
std::unique_ptr< wxLocale > mLocale
Definition: AudacityApp.h:182
std::unique_ptr< wxCmdLineParser > ParseCommandLine()
wxString GetDeviceInfo()
Get diagnostic information on all the available audio I/O devices.
Definition: AudioIO.cpp:3472
void OnMRUClear(wxCommandEvent &event)
wxRect GetNormalizedWindowState() const
Definition: Project.h:711
static wxString MkDir(const wxString &Str)
Definition: FileNames.cpp:72
std::unique_ptr< FileHistory > mRecentFiles
Definition: AudacityApp.h:180
std::unique_ptr< T, Destroyer< T >> Destroy_ptr
Definition: MemoryX.h:433
void InitAudioIO()
Definition: AudioIO.cpp:1116
wxWindow * MakeHijackPanel()
#define ID_RECENT_CLEAR
AudacityApp & wxGetApp()
END_EVENT_TABLE()
void OnHelpWelcome(const CommandContext &context)
Definition: Menus.cpp:8728
wxString defaultTempDir
Default temp directory.
Definition: AudacityApp.h:141
static PluginManager & Get()
static Importer & Get()
Definition: Import.cpp:71
static AudacityProject * OpenProject(AudacityProject *pProject, const wxString &fileNameArg, bool addtohistory=true)
Definition: Project.cpp:2960
bool ShowAutoRecoveryDialogIfNeeded(AudacityProject **pproj, bool *didRecoverAnything)
int FilterEvent(wxEvent &event) override
bool gIsQuitting
static void SetDontDeleteTempFiles()
Definition: DirManager.h:176
static void SetTempDir(const wxString &_temp)
Definition: DirManager.h:60
void SetToExtantDirectory(wxString &result, const wxString &dir)
static void DeleteClipboard()
Definition: Project.cpp:4888
std::unique_ptr< wxSingleInstanceChecker > mChecker
Definition: AudacityApp.h:184
a class wrapping reading and writing of arbitrary data in text or binary format to a file...
Definition: AutoRecovery.h:76
bool Terminate()
Definition: Import.cpp:114
void DeinitFFT()
Definition: FFT.cpp:115
AudacityProject * CreateNewAudacityProject()
Definition: Project.cpp:549