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