Audacity 3.2.0
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
19#include "AudacityApp.h"
20
21
22
23#if 0
24// This may be used to debug memory leaks.
25// See: Visual Leak Detector @ http://vld.codeplex.com/
26#include <vld.h>
27#endif
28
29#include <wx/setup.h> // for wxUSE_* macros
30#include <wx/wxcrtvararg.h>
31#include <wx/defs.h>
32#include <wx/evtloop.h>
33#include <wx/app.h>
34#include <wx/bitmap.h>
35#include <wx/docview.h>
36#include <wx/event.h>
37#include <wx/ipc.h>
38#include <wx/window.h>
39#include <wx/intl.h>
40#include <wx/menu.h>
41#include <wx/snglinst.h>
42#include <wx/splash.h>
43#include <wx/stdpaths.h>
44#include <wx/sysopt.h>
45#include <wx/fontmap.h>
46
47#include <wx/fs_zip.h>
48#include <wx/image.h>
49
50#include <wx/dir.h>
51#include <wx/file.h>
52#include <wx/filename.h>
53
54#ifdef __WXGTK__
55#include <unistd.h>
56#ifdef HAVE_GTK
57#include <gtk/gtk.h>
58#endif
59#endif
60
61// chmod, lstat, geteuid
62#ifdef __UNIX__
63#include <sys/types.h>
64#include <sys/file.h>
65#include <sys/stat.h>
66#include <stdio.h>
67#endif
68
69#if defined(__WXMSW__)
70#include <wx/msw/registry.h> // for wxRegKey
71#endif
72
73#include "AudacityLogger.h"
74#include "AboutDialog.h"
75#include "ActiveProject.h"
76#include "AColor.h"
77#include "AudacityFileConfig.h"
78#include "AudioIO.h"
79#include "Benchmark.h"
80#include "Clipboard.h"
81#include "CrashReport.h" // for HAS_CRASH_REPORT
84#include "widgets/ASlider.h"
85#include "FFmpeg.h"
86#include "Journal.h"
87//#include "LangChoice.h"
88#include "Languages.h"
89#include "Menus.h"
90#include "PathList.h"
91#include "PluginManager.h"
92#include "Project.h"
93#include "ProjectAudioIO.h"
94#include "ProjectAudioManager.h"
95#include "ProjectFileIO.h"
96#include "ProjectFileManager.h"
97#include "ProjectHistory.h"
98#include "ProjectManager.h"
99#include "ProjectSettings.h"
100#include "ProjectWindow.h"
101#include "ProjectWindows.h"
102#include "Screenshot.h"
103#include "Sequence.h"
104#include "SelectFile.h"
105#include "TempDirectory.h"
106#include "LoadThemeResources.h"
107#include "Track.h"
108#include "prefs/PrefsDialog.h"
109#include "Theme.h"
111#include "AutoRecoveryDialog.h"
112#include "SplashDialog.h"
113#include "FFT.h"
116#include "prefs/GUISettings.h"
117#include "tracks/ui/Scrubbing.h"
118#include "FileConfig.h"
119#include "widgets/FileHistory.h"
121#include "LogWindow.h"
125
126#if defined(HAVE_UPDATES_CHECK)
127# include "update/UpdateManager.h"
128#endif
129
130#ifdef HAS_NETWORKING
131#include "NetworkManager.h"
132#endif
133
134#ifdef EXPERIMENTAL_EASY_CHANGE_KEY_BINDINGS
135#include "prefs/KeyConfigPrefs.h"
136#endif
137
138//temporarily commented out till it is added to all projects
139//#include "Profiler.h"
140
141#include "ModuleManager.h"
142#include "PluginHost.h"
143
144#include "import/Import.h"
145
146#if defined(USE_BREAKPAD)
147#include "BreakpadConfigurer.h"
148#endif
149
150#ifdef EXPERIMENTAL_SCOREALIGN
152#endif
153
154#if 0
155#ifdef _DEBUG
156 #ifdef _MSC_VER
157 #undef THIS_FILE
158 static char*THIS_FILE= __FILE__;
159 #define new new(_NORMAL_BLOCK, THIS_FILE, __LINE__)
160 #endif
161#endif
162#endif
163
164// DA: Logo for Splash Screen
165#ifdef EXPERIMENTAL_DA
166#include "../images/DarkAudacityLogoWithName.xpm"
167#else
168#include "../images/AudacityLogoWithName.xpm"
169#endif
170
171#include <thread>
172
173#ifdef HAS_CUSTOM_URL_HANDLING
174#include "URLSchemesRegistry.h"
175
176// This prefix is used to encode parameters
177// in RPC used by single instance checker
178static wxString urlPrefix = wxT("url:");
179#endif
180
181
185
186#if 0
187#ifdef __WXGTK__
188static void wxOnAssert(const wxChar *fileName, int lineNumber, const wxChar *msg)
189{
190 if (msg)
191 wxPrintf("ASSERTION FAILED: %s\n%s: %d\n", (const char *)wxString(msg).mb_str(), (const char *)wxString(fileName).mb_str(), lineNumber);
192 else
193 wxPrintf("ASSERTION FAILED!\n%s: %d\n", (const char *)wxString(fileName).mb_str(), lineNumber);
194
195 // Force core dump
196 int *i = 0;
197 if (*i)
198 exit(1);
199
200 exit(0);
201}
202#endif
203#endif
204
205namespace {
206
208{
209 bool resetPrefs = false;
210 wxString langCode = gPrefs->Read(wxT("/Locale/Language"), wxEmptyString);
211 bool writeLang = false;
212
213 const wxFileName fn(
215 wxT("FirstTime.ini"));
216 if (fn.FileExists()) // it will exist if the (win) installer put it there
217 {
218 const wxString fullPath{fn.GetFullPath()};
219
220 auto pIni =
221 AudacityFileConfig::Create({}, {}, fullPath, {},
222 wxCONFIG_USE_LOCAL_FILE);
223 auto &ini = *pIni;
224
225 wxString lang;
226 if (ini.Read(wxT("/FromInno/Language"), &lang) && !lang.empty())
227 {
228 // Only change "langCode" if the language was actually specified in the ini file.
229 langCode = lang;
230 writeLang = true;
231
232 // Inno Setup doesn't allow special characters in the Name values, so "0" is used
233 // to represent the "@" character.
234 langCode.Replace(wxT("0"), wxT("@"));
235 }
236
237 ini.Read(wxT("/FromInno/ResetPrefs"), &resetPrefs, false);
238
239 bool gone = wxRemoveFile(fullPath); // remove FirstTime.ini
240 if (!gone)
241 {
243 XO("Failed to remove %s").Format(fullPath),
244 XO("Failed!"));
245 }
246 }
247
248 // Use the system default language if one wasn't specified or if the user selected System.
249 if (langCode.empty())
250 langCode =
252
253 langCode = GUISettings::SetLang( langCode );
254
255 // User requested that the preferences be completely reset
256 if (resetPrefs)
257 {
258 // pop up a dialogue
259 auto prompt = XO(
260"Reset Preferences?\n\nThis is a one-time question, after an 'install' where you asked to have the Preferences reset.");
261 int action = AudacityMessageBox(
262 prompt,
263 XO("Reset Audacity Preferences"),
264 wxYES_NO, NULL);
265 if (action == wxYES) // reset
266 {
268 writeLang = true;
269 }
270 }
271
272 // Save the specified language
273 if (writeLang)
274 {
275 gPrefs->Write(wxT("/Locale/Language"), langCode);
276 }
277
278 // In AUdacity 2.1.0 support for the legacy 1.2.x preferences (depreciated since Audacity
279 // 1.3.1) is dropped. As a result we can drop the import flag
280 // first time this version of Audacity is run we try to migrate
281 // old preferences.
282 bool newPrefsInitialized = false;
283 gPrefs->Read(wxT("/NewPrefsInitialized"), &newPrefsInitialized, false);
284 if (newPrefsInitialized) {
285 gPrefs->DeleteEntry(wxT("/NewPrefsInitialized"), true); // take group as well if empty
286 }
287
288 // record the Prefs version for future checking (this has not been used for a very
289 // long time).
290 gPrefs->Write(wxT("/PrefsVersion"), wxString(wxT(AUDACITY_PREFS_VERSION_STRING)));
291
292 // Check if some prefs updates need to happen based on audacity version.
293 // Unfortunately we can't use the PrefsVersion prefs key because that resets things.
294 // In the future we may want to integrate that better.
295 // these are done on a case-by-case basis for now so they must be backwards compatible
296 // (meaning the changes won't mess audacity up if the user goes back to an earlier version)
297 int vMajor = gPrefs->Read(wxT("/Version/Major"), (long) 0);
298 int vMinor = gPrefs->Read(wxT("/Version/Minor"), (long) 0);
299 int vMicro = gPrefs->Read(wxT("/Version/Micro"), (long) 0);
300
301 gPrefs->SetVersionKeysInit(vMajor, vMinor, vMicro); // make a note of these initial values
302 // for use by ToolManager::ReadConfig()
303
304 // These integer version keys were introduced april 4 2011 for 1.3.13
305 // The device toolbar needs to be enabled due to removal of source selection features in
306 // the mixer toolbar.
307 if ((vMajor < 1) ||
308 (vMajor == 1 && vMinor < 3) ||
309 (vMajor == 1 && vMinor == 3 && vMicro < 13)) {
310
311
312 // Do a full reset of the Device Toolbar to get it on the screen.
313 if (gPrefs->Exists(wxT("/GUI/ToolBars/Device")))
314 gPrefs->DeleteGroup(wxT("/GUI/ToolBars/Device"));
315
316 // We keep the mixer toolbar prefs (shown/not shown)
317 // the width of the mixer toolbar may have shrunk, the prefs will keep the larger value
318 // if the user had a device that had more than one source.
319 if (gPrefs->Exists(wxT("/GUI/ToolBars/Mixer"))) {
320 // Use the default width
321 gPrefs->Write(wxT("/GUI/ToolBars/Mixer/W"), -1);
322 }
323 }
324
325 // In 2.1.0, the Meter toolbar was split and lengthened, but strange arrangements happen
326 // if upgrading due to the extra length. So, if a user is upgrading, use the pre-2.1.0
327 // lengths, but still use the NEW split versions.
328 if (gPrefs->Exists(wxT("/GUI/ToolBars/Meter")) &&
329 !gPrefs->Exists(wxT("/GUI/ToolBars/CombinedMeter"))) {
330
331 // Read in all of the existing values
332 long dock, order, show, x, y, w, h;
333 gPrefs->Read(wxT("/GUI/ToolBars/Meter/Dock"), &dock, -1);
334 gPrefs->Read(wxT("/GUI/ToolBars/Meter/Order"), &order, -1);
335 gPrefs->Read(wxT("/GUI/ToolBars/Meter/Show"), &show, -1);
336 gPrefs->Read(wxT("/GUI/ToolBars/Meter/X"), &x, -1);
337 gPrefs->Read(wxT("/GUI/ToolBars/Meter/Y"), &y, -1);
338 gPrefs->Read(wxT("/GUI/ToolBars/Meter/W"), &w, -1);
339 gPrefs->Read(wxT("/GUI/ToolBars/Meter/H"), &h, -1);
340
341 // "Order" must be adjusted since we're inserting two NEW toolbars
342 if (dock > 0) {
343 wxString oldPath = gPrefs->GetPath();
344 gPrefs->SetPath(wxT("/GUI/ToolBars"));
345
346 wxString bar;
347 long ndx = 0;
348 bool cont = gPrefs->GetFirstGroup(bar, ndx);
349 while (cont) {
350 long o;
351 if (gPrefs->Read(bar + wxT("/Order"), &o) && o >= order) {
352 gPrefs->Write(bar + wxT("/Order"), o + 2);
353 }
354 cont = gPrefs->GetNextGroup(bar, ndx);
355 }
356 gPrefs->SetPath(oldPath);
357
358 // And override the height
359 h = 27;
360 }
361
362 // Write the split meter bar values
363 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/Dock"), dock);
364 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/Order"), order);
365 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/Show"), show);
366 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/X"), -1);
367 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/Y"), -1);
368 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/W"), w);
369 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/H"), h);
370 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/Dock"), dock);
371 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/Order"), order + 1);
372 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/Show"), show);
373 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/X"), -1);
374 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/Y"), -1);
375 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/W"), w);
376 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/H"), h);
377
378 // And hide the old combined meter bar
379 gPrefs->Write(wxT("/GUI/ToolBars/Meter/Dock"), -1);
380 }
381
382 // Upgrading pre 2.2.0 configs we assume extended set of defaults.
383 if ((0<vMajor && vMajor < 2) ||
384 (vMajor == 2 && vMinor < 2))
385 {
386 gPrefs->Write(wxT("/GUI/Shortcuts/FullDefaults"),1);
387 }
388
389 // Upgrading pre 2.4.0 configs, the selection toolbar is now split.
390 if ((0<vMajor && vMajor < 2) ||
391 (vMajor == 2 && vMinor < 4))
392 {
393 gPrefs->Write(wxT("/GUI/Toolbars/Selection/W"),"");
394 gPrefs->Write(wxT("/GUI/Toolbars/SpectralSelection/W"),"");
395 gPrefs->Write(wxT("/GUI/Toolbars/Time/X"),-1);
396 gPrefs->Write(wxT("/GUI/Toolbars/Time/Y"),-1);
397 gPrefs->Write(wxT("/GUI/Toolbars/Time/H"),55);
398 gPrefs->Write(wxT("/GUI/Toolbars/Time/W"),251);
399 gPrefs->Write(wxT("/GUI/Toolbars/Time/DockV2"),2);
400 gPrefs->Write(wxT("/GUI/Toolbars/Time/Dock"),2);
401 gPrefs->Write(wxT("/GUI/Toolbars/Time/Path"),"0,1");
402 gPrefs->Write(wxT("/GUI/Toolbars/Time/Show"),1);
403 }
404
405 if (std::pair{ vMajor, vMinor } < std::pair{ 3, 1 } ) {
406 // Reset the control toolbar
407 gPrefs->Write(wxT("/GUI/Toolbars/Control/W"), -1);
408 }
409
410 if(std::pair{vMajor, vMinor} < std::pair{3, 2})
411 {
412 if(gPrefs->Exists(wxT("/GUI/ToolBars")))
413 gPrefs->DeleteGroup(wxT("/GUI/ToolBars"));
414 if(gPrefs->Exists(wxT("Window")))
415 gPrefs->DeleteGroup(wxT("Window"));
416 if(gPrefs->Exists("/GUI/ShowSplashScreen"))
417 gPrefs->DeleteEntry("/GUI/ShowSplashScreen");
418 if(gPrefs->Exists("/GUI/Help"))
419 gPrefs->DeleteEntry("/GUI/Help");
420 }
421
422 // write out the version numbers to the prefs file for future checking
423 gPrefs->Write(wxT("/Version/Major"), AUDACITY_VERSION);
424 gPrefs->Write(wxT("/Version/Minor"), AUDACITY_RELEASE);
425 gPrefs->Write(wxT("/Version/Micro"), AUDACITY_REVISION);
426
427 gPrefs->Flush();
428}
429
430#if defined(USE_BREAKPAD)
431void InitBreakpad()
432{
433 wxFileName databasePath;
434 databasePath.SetPath(FileNames::StateDir());
435 databasePath.AppendDir("crashreports");
436 databasePath.Mkdir(wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL);
437
438 if(databasePath.DirExists())
439 {
440 const auto sentryRelease = wxString::Format(
441 "[email protected]%d.%d.%d", AUDACITY_VERSION, AUDACITY_RELEASE, AUDACITY_REVISION
442 );
443 BreakpadConfigurer configurer;
444 configurer.SetDatabasePathUTF8(databasePath.GetPath().ToUTF8().data())
445 .SetSenderPathUTF8(wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath().ToUTF8().data())
446 #if defined(CRASH_REPORT_URL)
447 .SetReportURL(CRASH_REPORT_URL)
448 #endif
449 .SetParameters({
450 { "version", wxString(AUDACITY_VERSION_STRING).ToUTF8().data() },
451 { "sentry[release]", sentryRelease.ToUTF8().data() }
452 })
453 .Start();
454 }
455}
456#endif
457}
458
459static bool gInited = false;
460static bool gIsQuitting = false;
461
462static bool CloseAllProjects( bool force )
463{
465 auto cleanup = finally([]{ ProjectManager::SetClosingAll(false); });
466 while (AllProjects{}.size())
467 {
468 // Closing the project has global side-effect
469 // of deletion from gAudacityProjects
470 if ( force )
471 {
472 GetProjectFrame( **AllProjects{}.begin() ).Close(true);
473 }
474 else
475 {
476 if (! GetProjectFrame( **AllProjects{}.begin() ).Close())
477 return false;
478 }
479 }
480 return true;
481}
482
483static void QuitAudacity(bool bForce)
484{
485 // guard against recursion
486 if (gIsQuitting)
487 return;
488
489 gIsQuitting = true;
490
491 wxTheApp->SetExitOnFrameDelete(true);
492
493 // Try to close each open window. If the user hits Cancel
494 // in a Save Changes dialog, don't continue.
495 // BG: unless force is true
496
497 // BG: Are there any projects open?
498 //- if (!AllProjects{}.empty())
499/*start+*/
500 if (AllProjects{}.empty())
501 {
502#ifdef __WXMAC__
504#endif
505 }
506 else
507/*end+*/
508 {
509 if (AllProjects{}.size())
510 // PRL: Always did at least once before close might be vetoed
511 // though I don't know why that is important
513 bool closedAll = CloseAllProjects( bForce );
514 if ( !closedAll )
515 {
516 gIsQuitting = false;
517 return;
518 }
519 }
520
522
523#ifdef EXPERIMENTAL_SCOREALIGN
524 CloseScoreAlignDialog();
525#endif
527
528 // Logger window is always destroyed on macOS,
529 // on other platforms - it prevents the runloop
530 // termination when exiting is requested
531 #if !defined(__WXMAC__)
534 #endif
535
536 //print out profile if we have one by deleting it
537 //temporarily commented out till it is added to all projects
538 //DELETE Profiler::Instance();
539
540 // Save last log for diagnosis
541 auto logger = AudacityLogger::Get();
542 if (logger)
543 {
544 wxFileName logFile(FileNames::DataDir(), wxT("lastlog.txt"));
545 logger->SaveLog(logFile.GetFullPath());
546 }
547
548 //remove our logger
549 std::unique_ptr<wxLog>{ wxLog::SetActiveTarget(NULL) }; // DELETE
550
551 if (bForce)
552 {
553 wxExit();
554 }
555}
556
557static void QuitAudacity()
558{
559 QuitAudacity(false);
560}
561
562#if defined(__WXGTK__) && defined(HAVE_GTK)
563
565// Provide the ability to receive notification from the session manager
566// when the user is logging out or shutting down.
567//
568// Most of this was taken from nsNativeAppSupportUnix.cpp from Mozilla.
570
571// TODO: May need updating. Is this code too obsolete (relying on Gnome2 so's) to be
572// worth keeping anymore?
573// CB suggests we use libSM directly ref:
574// http://www.x.org/archive/X11R7.7/doc/libSM/SMlib.html#The_Save_Yourself_Callback
575
576#include <dlfcn.h>
577/* There is a conflict between the type names used in Glib >= 2.21 and those in
578 * wxGTK (http://trac.wxwidgets.org/ticket/10883)
579 * Happily we can avoid the hack, as we only need some of the headers, not
580 * the full GTK headers
581 */
582#include <glib-object.h>
583
584typedef struct _GnomeProgram GnomeProgram;
585typedef struct _GnomeModuleInfo GnomeModuleInfo;
586typedef struct _GnomeClient GnomeClient;
587
588typedef enum
589{
590 GNOME_SAVE_GLOBAL,
591 GNOME_SAVE_LOCAL,
592 GNOME_SAVE_BOTH
593} GnomeSaveStyle;
594
595typedef enum
596{
597 GNOME_INTERACT_NONE,
598 GNOME_INTERACT_ERRORS,
599 GNOME_INTERACT_ANY
600} GnomeInteractStyle;
601
602typedef enum
603{
604 GNOME_DIALOG_ERROR,
605 GNOME_DIALOG_NORMAL
606} GnomeDialogType;
607
608typedef GnomeProgram * (*_gnome_program_init_fn)(const char *,
609 const char *,
610 const GnomeModuleInfo *,
611 int,
612 char **,
613 const char *,
614 ...);
615typedef const GnomeModuleInfo * (*_libgnomeui_module_info_get_fn)();
616typedef GnomeClient * (*_gnome_master_client_fn)(void);
617typedef void (*GnomeInteractFunction)(GnomeClient *,
618 gint,
619 GnomeDialogType,
620 gpointer);
621typedef void (*_gnome_client_request_interaction_fn)(GnomeClient *,
622 GnomeDialogType,
623 GnomeInteractFunction,
624 gpointer);
625typedef void (*_gnome_interaction_key_return_fn)(gint, gboolean);
626
627static _gnome_client_request_interaction_fn gnome_client_request_interaction;
628static _gnome_interaction_key_return_fn gnome_interaction_key_return;
629
630static void interact_cb(GnomeClient * /* client */,
631 gint key,
632 GnomeDialogType /* type */,
633 gpointer /* data */)
634{
635 wxCloseEvent e(wxEVT_QUERY_END_SESSION, wxID_ANY);
636 e.SetEventObject(&wxGetApp());
637 e.SetCanVeto(true);
638
639 wxGetApp().ProcessEvent(e);
640
641 gnome_interaction_key_return(key, e.GetVeto());
642}
643
644static gboolean save_yourself_cb(GnomeClient *client,
645 gint /* phase */,
646 GnomeSaveStyle /* style */,
647 gboolean shutdown,
648 GnomeInteractStyle interact,
649 gboolean /* fast */,
650 gpointer /* user_data */)
651{
652 if (!shutdown || interact != GNOME_INTERACT_ANY) {
653 return TRUE;
654 }
655
656 if (AllProjects{}.empty()) {
657 return TRUE;
658 }
659
660 gnome_client_request_interaction(client,
661 GNOME_DIALOG_NORMAL,
662 interact_cb,
663 NULL);
664
665 return TRUE;
666}
667
668class GnomeShutdown
669{
670 public:
671 GnomeShutdown()
672 {
673 mArgv[0].reset(strdup("Audacity"));
674
675 mGnomeui = dlopen("libgnomeui-2.so.0", RTLD_NOW);
676 if (!mGnomeui) {
677 return;
678 }
679
680 mGnome = dlopen("libgnome-2.so.0", RTLD_NOW);
681 if (!mGnome) {
682 return;
683 }
684
685 _gnome_program_init_fn gnome_program_init = (_gnome_program_init_fn)
686 dlsym(mGnome, "gnome_program_init");
687 _libgnomeui_module_info_get_fn libgnomeui_module_info_get = (_libgnomeui_module_info_get_fn)
688 dlsym(mGnomeui, "libgnomeui_module_info_get");
689 _gnome_master_client_fn gnome_master_client = (_gnome_master_client_fn)
690 dlsym(mGnomeui, "gnome_master_client");
691
692 gnome_client_request_interaction = (_gnome_client_request_interaction_fn)
693 dlsym(mGnomeui, "gnome_client_request_interaction");
694 gnome_interaction_key_return = (_gnome_interaction_key_return_fn)
695 dlsym(mGnomeui, "gnome_interaction_key_return");
696
697
698 if (!gnome_program_init || !libgnomeui_module_info_get) {
699 return;
700 }
701
702 gnome_program_init(mArgv[0].get(),
703 "1.0",
704 libgnomeui_module_info_get(),
705 1,
706 reinterpret_cast<char**>(mArgv),
707 NULL);
708
709 mClient = gnome_master_client();
710 if (mClient == NULL) {
711 return;
712 }
713
714 g_signal_connect(mClient, "save-yourself", G_CALLBACK(save_yourself_cb), NULL);
715 }
716
717 virtual ~GnomeShutdown()
718 {
719 // Do not dlclose() the libraries here lest you want segfaults...
720 }
721
722 private:
723
724 MallocString<> mArgv[1];
725 void *mGnomeui;
726 void *mGnome;
727 GnomeClient *mClient;
728};
729
730// This variable exists to call the constructor and
731// connect a signal for the 'save-yourself' message.
732GnomeShutdown GnomeShutdownInstance;
733
734#endif
735
736// Where drag/drop or "Open With" filenames get stored until
737// the timer routine gets around to picking them up.
738static wxArrayString ofqueue;
739
740//
741// DDE support for opening multiple files with one instance
742// of Audacity.
743//
744
745#define IPC_APPL wxT("audacity")
746#define IPC_TOPIC wxT("System")
747
748class IPCConn final : public wxConnection
749{
750public:
752 : wxConnection()
753 {
754 };
755
757 {
758 };
759
760 bool OnExec(const wxString & WXUNUSED(topic),
761 const wxString & data)
762 {
763 // Add the filename to the queue. It will be opened by
764 // the OnTimer() event when it is safe to do so.
765 ofqueue.push_back(data);
766
767 return true;
768 }
769};
770
771class IPCServ final : public wxServer
772{
773public:
774 IPCServ(const wxString & appl)
775 : wxServer()
776 {
777 Create(appl);
778 };
779
781 {
782 };
783
784 wxConnectionBase *OnAcceptConnection(const wxString & topic) override
785 {
786 if (topic != IPC_TOPIC) {
787 return NULL;
788 }
789
790 // Trust wxWidgets framework to DELETE it
791 return safenew IPCConn();
792 };
793};
794
795#if defined(__WXMAC__)
796
797IMPLEMENT_APP_NO_MAIN(AudacityApp)
799
800#include <ApplicationServices/ApplicationServices.h>
801
802namespace
803{
805}
806
807int main(int argc, char *argv[])
808{
809 wxDISABLE_DEBUG_SUPPORT();
810
811 wxCmdLineArgsArray argsArray;
812 argsArray.Init(argc, argv);
813 if(PluginHost::IsHostProcess(argc, argsArray))
814 {
815 sOSXIsGUIApplication = false;
816 ProcessSerialNumber psn = { 0, kCurrentProcess };
817 TransformProcessType(&psn, kProcessTransformToUIElementApplication);
818 }
819
820 return wxEntry(argc, argv);
821}
822
823#elif defined(__WXGTK__) && defined(NDEBUG)
824
825IMPLEMENT_APP_NO_MAIN(AudacityApp)
827
828int main(int argc, char *argv[])
829{
830 wxDISABLE_DEBUG_SUPPORT();
831
832 // Bug #1986 workaround - This doesn't actually reduce the number of
833 // messages, it simply hides them in Release builds. We'll probably
834 // never be able to get rid of the messages entirely, but we should
835 // look into what's causing them, so allow them to show in Debug
836 // builds.
837 stdout = freopen("/dev/null", "w", stdout);
838 stderr = freopen("/dev/null", "w", stderr);
839
840 return wxEntry(argc, argv);
841}
842
843#else
844IMPLEMENT_APP(AudacityApp)
845#endif
846
847#ifdef __WXMAC__
848
849// in response of an open-document apple event
850void AudacityApp::MacOpenFile(const wxString &fileName)
851{
852 ofqueue.push_back(fileName);
853}
854
855// in response of a print-document apple event
856void AudacityApp::MacPrintFile(const wxString &fileName)
857{
858 ofqueue.push_back(fileName);
859}
860
861// in response of a open-application apple event
863{
864 if (!gInited)
865 return;
866
867 // This method should only be used on the Mac platform
868 // when no project windows are open.
869
870 if (AllProjects{}.empty())
871 (void) ProjectManager::New();
872}
873
874#ifdef HAS_CUSTOM_URL_HANDLING
875void AudacityApp::MacOpenURL (const wxString& url)
876{
877 if(!url.IsEmpty())
878 ofqueue.push_back(urlPrefix + url);
879}
880#endif
881
882#endif //__WXMAC__
883
884// IPC communication
885#define ID_IPC_SERVER 6200
886#define ID_IPC_SOCKET 6201
887
888// we don't really care about the timer id, but set this value just in case we do in the future
889#define kAudacityAppTimerID 0
890
891BEGIN_EVENT_TABLE(AudacityApp, wxApp)
892 EVT_IDLE( AudacityApp::OnIdle )
893
894 EVT_QUERY_END_SESSION(AudacityApp::OnQueryEndSession)
895 EVT_END_SESSION(AudacityApp::OnEndSession)
896
898#ifdef __WXMAC__
903#endif
904
905 // Associate the handler with the menu id on all operating systems, even
906 // if they don't have an application menu bar like in macOS, so that
907 // other parts of the program can send the application a shut-down
908 // event
910
911#ifndef __WXMSW__
914#endif
915
916 // Recent file event handlers.
918 EVT_MENU_RANGE(FileHistory::ID_RECENT_FIRST, FileHistory::ID_RECENT_LAST,
919 AudacityApp::OnMRUFile)
920
921 // Handle AppCommandEvents (usually from a script)
922 EVT_APP_COMMAND(wxID_ANY, AudacityApp::OnReceiveCommand)
923
924 // Global ESC key handling
925 EVT_KEY_DOWN(AudacityApp::OnKeyDown)
927
928// backend for OnMRUFile
929// TODO: Would be nice to make this handle not opening a file with more panache.
930// - Inform the user if DefaultOpenPath not set.
931// - Switch focus to correct instance of project window, if already open.
932bool AudacityApp::MRUOpen(const FilePath &fullPathStr) {
933 // Most of the checks below are copied from ProjectManager::OpenFiles.
934 // - some rationalisation might be possible.
935
936 auto pProj = GetActiveProject().lock();
937 auto proj = pProj.get();
938
939 if (!fullPathStr.empty())
940 {
941 // verify that the file exists
942 if (wxFile::Exists(fullPathStr))
943 {
944 FileNames::UpdateDefaultPath(FileNames::Operation::Open, ::wxPathOnly(fullPathStr));
945
946 // Make sure it isn't already open.
947 // Test here even though AudacityProject::OpenFile() also now checks, because
948 // that method does not return the bad result.
949 // That itself may be a FIXME.
950 if (ProjectFileManager::IsAlreadyOpen(fullPathStr))
951 return false;
952
954 ( void ) ProjectManager::OpenProject( proj, fullPathStr,
955 true /* addtohistory */, false /* reuseNonemptyProject */ );
956 }
957 else {
958 // File doesn't exist - remove file from history
960 XO(
961"%s could not be found.\n\nIt has been removed from the list of recent files.")
962 .Format(fullPathStr) );
963 return(false);
964 }
965 }
966 return(true);
967}
968
969bool AudacityApp::SafeMRUOpen(const wxString &fullPathStr)
970{
971 return GuardedCall< bool >( [&]{ return MRUOpen( fullPathStr ); } );
972}
973
974void AudacityApp::OnMRUClear(wxCommandEvent& WXUNUSED(event))
975{
977}
978
979//vvv Basically, anything from Recent Files is treated as a .aup3, until proven otherwise,
980// then it tries to Import(). Very questionable handling, imo.
981// Better, for example, to check the file type early on.
982void AudacityApp::OnMRUFile(wxCommandEvent& event) {
983 int n = event.GetId() - FileHistory::ID_RECENT_FIRST;
984 auto &history = FileHistory::Global();
985 const auto &fullPathStr = history[ n ];
986
987 // Try to open only if not already open.
988 // Test IsAlreadyOpen() here even though AudacityProject::MRUOpen() also now checks,
989 // because we don't want to Remove() just because it already exists,
990 // and AudacityApp::OnMacOpenFile() calls MRUOpen() directly.
991 // that method does not return the bad result.
992 // PRL: Don't call SafeMRUOpen
993 // -- if open fails for some exceptional reason of resource exhaustion that
994 // the user can correct, leave the file in history.
995 if (!ProjectFileManager::IsAlreadyOpen(fullPathStr) && !MRUOpen(fullPathStr))
996 history.Remove(n);
997}
998
999void AudacityApp::OnTimer(wxTimerEvent& WXUNUSED(event))
1000{
1001 // Filenames are queued when Audacity receives a few of the
1002 // AppleEvent messages (via wxWidgets). So, open any that are
1003 // in the queue and clean the queue.
1004 if (gInited) {
1005 if (ofqueue.size()) {
1006 // Load each file on the queue
1007 while (ofqueue.size()) {
1008 wxString name;
1009 name.swap(ofqueue[0]);
1010 ofqueue.erase( ofqueue.begin() );
1011
1012 // Get the user's attention if no file name was specified
1013 if (name.empty()) {
1014 // Get the users attention
1015 if (auto project = GetActiveProject().lock()) {
1016 auto &window = GetProjectFrame( *project );
1017 window.Maximize();
1018 window.Raise();
1019 window.RequestUserAttention();
1020 }
1021 continue;
1022 }
1023
1024 #ifdef HAS_CUSTOM_URL_HANDLING
1025 if (name.StartsWith(urlPrefix))
1026 {
1027 const auto utf8Url = name.ToUTF8();
1028 const size_t prefixSize = urlPrefix.Length();
1029
1030 if (utf8Url.length() <= prefixSize)
1031 continue;
1032
1034 { utf8Url.data() + prefixSize,
1035 utf8Url.length() - prefixSize });
1036 }
1037 else
1038 #endif
1039 // TODO: Handle failures better.
1040 // Some failures are OK, e.g. file not found, just would-be-nices to do better,
1041 // so FAIL_MSG is more a case of an enhancement request than an actual problem.
1042 // LL: In all but one case an appropriate message is already displayed. The
1043 // instance that a message is NOT displayed is when a failure to write
1044 // to the config file has occurred.
1045 // PRL: Catch any exceptions, don't try this file again, continue to
1046 // other files.
1047 if (!SafeMRUOpen(name)) {
1048 // Just log it. Assertion failure is not appropriate on valid
1049 // defensive path against bad file data.
1050 wxLogMessage(wxT("MRUOpen failed"));
1051 }
1052 }
1053 }
1054 }
1055}
1056
1057#if defined(__WXMSW__)
1058#define WL(lang, sublang) (lang), (sublang),
1059#else
1060#define WL(lang,sublang)
1061#endif
1062
1063#if wxCHECK_VERSION(3, 0, 1) && !wxCHECK_VERSION(3, 1, 6)
1064wxLanguageInfo userLangs[] =
1065{
1066 // Included upstream in version 3.1.6
1067 { wxLANGUAGE_USER_DEFINED, wxT("eu"), WL(0, SUBLANG_DEFAULT) wxT("Basque"), wxLayout_LeftToRight },
1068};
1069#endif
1070
1072{
1073#if defined(HAS_CRASH_REPORT)
1074 CrashReport::Generate(wxDebugReport::Context_Exception);
1075#endif
1076
1077 exit(-1);
1078}
1079
1080
1081#ifdef _MSC_VER
1082// If this is compiled with MSVC (Visual Studio)
1083#pragma warning( push )
1084#pragma warning( disable : 4702) // unreachable code warning.
1085#endif //_MSC_VER
1086
1088{
1089 // This function is invoked from catch blocks in the wxWidgets framework,
1090 // and throw; without argument re-throws the exception being handled,
1091 // letting us dispatch according to its type.
1092
1093 try { throw; }
1094 catch ( AudacityException &e ) {
1095 (void)e;// Compiler food
1096 // Here is the catch-all for our own exceptions
1097
1098 // Use CallAfter to delay this to the next pass of the event loop,
1099 // rather than risk doing it inside stack unwinding.
1100 auto pProject = ::GetActiveProject().lock();
1101 auto pException = std::current_exception();
1102 CallAfter( [pException, pProject] {
1103
1104 // Restore the state of the project to what it was before the
1105 // failed operation
1106 if (pProject) {
1107 ProjectHistory::Get( *pProject ).RollbackState();
1108
1109 // Forget pending changes in the TrackList
1110 TrackList::Get( *pProject ).ClearPendingTracks();
1111
1112 ProjectWindow::Get( *pProject ).RedrawProject();
1113 }
1114
1115 // Give the user an alert
1116 try { std::rethrow_exception( pException ); }
1117 catch( AudacityException &e )
1118 { e.DelayedHandlerAction(); }
1119
1120 } );
1121
1122 // Don't quit the program
1123 return true;
1124 }
1125 catch ( ... ) {
1126 // There was some other type of exception we don't know.
1127 // Let the inherited function do throw; again and whatever else it does.
1128 return wxApp::OnExceptionInMainLoop();
1129 }
1130 // Shouldn't ever reach this line
1131 return false;
1132}
1133#ifdef _MSC_VER
1134#pragma warning( pop )
1135#endif //_MSC_VER
1136
1138{
1139}
1140
1141bool AudacityApp::Initialize(int& argc, wxChar** argv)
1142{
1143 if(!PluginHost::IsHostProcess(argc, argv))
1144 {
1145#if defined(USE_BREAKPAD)
1146 InitBreakpad();
1147 // Do not capture crashes in debug builds
1148#elif !defined(_DEBUG)
1149#if defined(HAS_CRASH_REPORT)
1150#if defined(wxUSE_ON_FATAL_EXCEPTION) && wxUSE_ON_FATAL_EXCEPTION
1151 wxHandleFatalExceptions();
1152#endif
1153#endif
1154#endif
1155 }
1156 return wxApp::Initialize(argc, argv);
1157}
1158
1159#ifdef __WXMAC__
1161{
1162 return sOSXIsGUIApplication;
1163}
1164#endif
1165
1167{
1168}
1169
1170// Some of the many initialization steps
1172{
1173 // Inject basic GUI services behind the facade
1174 {
1175 static wxWidgetsBasicUI uiServices;
1176 (void)BasicUI::Install(&uiServices);
1177 }
1178
1179 // Fire up SQLite
1181 this->CallAfter([]{
1183 XO("SQLite library failed to initialize. Audacity cannot continue.") );
1184 QuitAudacity( true );
1185 });
1186
1187
1188 // cause initialization of wxWidgets' global logger target
1189 (void) AudacityLogger::Get();
1190
1191#if defined(__WXMAC__)
1192 // Disable window animation
1193 wxSystemOptions::SetOption(wxMAC_WINDOW_PLAIN_TRANSITION, 1);
1194#endif
1195
1196 // Some GTK themes produce larger combo boxes that make them taller
1197 // than our single toolbar height restriction. This will remove some
1198 // of the extra space themes add.
1199#if defined(__WXGTK3__) && defined(HAVE_GTK)
1200 GtkWidget *combo = gtk_combo_box_new();
1201 GtkCssProvider *provider = gtk_css_provider_new();
1202 gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(provider),
1203 ".linked entry,\n"
1204 ".linked button,\n"
1205 ".linked combobox box.linked button,\n"
1206 ".horizontal.linked entry,\n"
1207 ".horizontal.linked button,\n"
1208 ".horizontal.linked combobox box.linked button,\n"
1209 "combobox {\n"
1210 " padding-top: 0px;\n"
1211 " padding-bottom: 0px;\n"
1212 " padding-left: 4px;\n"
1213 " padding-right: 4px;\n"
1214 " margin: 0px;\n"
1215 " font-size: 95%;\n"
1216 "}", -1, NULL);
1217 gtk_style_context_add_provider_for_screen(gtk_widget_get_screen(combo),
1218 GTK_STYLE_PROVIDER (provider),
1219 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1220 g_object_unref(provider);
1221 g_object_unref(combo);
1222#elif defined(__WXGTK__) && defined(HAVE_GTK)
1223 gtk_rc_parse_string("style \"audacity\" {\n"
1224 " GtkButton::inner_border = { 0, 0, 0, 0 }\n"
1225 " GtkEntry::inner_border = { 0, 0, 0, 0 }\n"
1226 " xthickness = 4\n"
1227 " ythickness = 0\n"
1228 "}\n"
1229 "widget_class \"*GtkCombo*\" style \"audacity\"");
1230#endif
1231
1232 wxTheApp->SetAppName(AppName);
1233 // Explicitly set since OSX will use it for the "Quit" menu item
1234 wxTheApp->SetAppDisplayName(AppName);
1235 wxTheApp->SetVendorName(AppName);
1236
1237 ::wxInitAllImageHandlers();
1238
1239 // AddHandler takes ownership
1240 wxFileSystem::AddHandler(safenew wxZipFSHandler);
1241}
1242
1243// The `main program' equivalent, creating the windows and returning the
1244// main frame
1246{
1247 // JKC: ANSWER-ME: Who actually added the event loop guarantor?
1248 // Although 'blame' says Leland, I think it came from a donated patch.
1249
1250 // PRL: It was added by LL at 54676a72285ba7ee3a69920e91fa390a71ef10c9 :
1251 // " Ensure OnInit() has an event loop
1252 // And allow events to flow so the splash window updates under GTK"
1253 // then mistakenly lost in the merge at
1254 // 37168ebbf67ae869ab71a3b5cbbf1d2a48e824aa
1255 // then restored at 7687972aa4b2199f0717165235f3ef68ade71e08
1256
1257 // Ensure we have an event loop during initialization
1258 wxEventLoopGuarantor eventLoop;
1259
1260 OnInit0();
1261
1263
1264 // Define languages for which we have translations, but that are not yet
1265 // supported by wxWidgets.
1266 //
1267 // TODO: The whole Language initialization really need to be reworked.
1268 // It's all over the place.
1269#if wxCHECK_VERSION(3, 0, 1) && !wxCHECK_VERSION(3, 1, 6)
1270 for (size_t i = 0, cnt = WXSIZEOF(userLangs); i < cnt; i++)
1271 {
1272 wxLocale::AddLanguage(userLangs[i]);
1273 }
1274#endif
1275
1276 // Initialize preferences and language
1277 {
1278 wxFileName configFileName{ FileNames::Configuration() };
1279 auto appName = wxTheApp->GetAppName();
1281 appName, wxEmptyString,
1282 configFileName.GetFullPath(),
1283 wxEmptyString, wxCONFIG_USE_LOCAL_FILE) );
1285 }
1286
1288
1289 {
1290 wxBusyCursor busy;
1292 }
1293
1294 // AColor depends on theTheme.
1295 AColor::Init();
1296
1297 // If this fails, we must exit the program.
1298 if (!InitTempDir()) {
1300 return false;
1301 }
1302
1304
1305#ifdef __WXMAC__
1306 // Bug2437: When files are opened from Finder and another instance of
1307 // Audacity is running, we must return from OnInit() to wxWidgets before
1308 // MacOpenFile is called, informing us of the paths that need to be
1309 // opened. So use CallAfter() to delay the rest of initialization.
1310 // See CreateSingleInstanceChecker() where we send those paths over a
1311 // socket to the prior instance.
1312
1313 // This call is what probably makes the sleep unnecessary:
1315
1316 using namespace std::chrono;
1317 // This sleep may be unnecessary, but it is harmless. It less NS framework
1318 // events arrive on another thread, but it might not always be long enough.
1319 std::this_thread::sleep_for(100ms);
1320 CallAfter([this]{
1321 if (!InitPart2())
1322 exit(-1);
1323 });
1324 return true;
1325#else
1326 return InitPart2();
1327#endif
1328}
1329
1331{
1332#if defined(__WXMAC__)
1333 SetExitOnFrameDelete(false);
1334#endif
1335
1336 // Make sure the temp dir isn't locked by another process.
1337 {
1338 auto key =
1339 PreferenceKey(FileNames::Operation::Temp, FileNames::PathType::_None);
1340 auto temp = gPrefs->Read(key);
1341 if (temp.empty() || !CreateSingleInstanceChecker(temp)) {
1343 return false;
1344 }
1345 }
1346
1347 //<<<< Try to avoid dialogs before this point.
1348 // The reason is that InitTempDir starts the single instance checker.
1349 // If we're waiitng in a dialog before then we can very easily
1350 // start multiple instances, defeating the single instance checker.
1351
1352 // Initialize the CommandHandler
1354
1355 // Initialize the ModuleManager, including loading found modules
1357
1358 // Initialize the PluginManager
1359 PluginManager::Get().Initialize( [](const FilePath &localFileName){
1360 return AudacityFileConfig::Create({}, {}, localFileName); } );
1361
1362 // Parse command line and handle options that might require
1363 // immediate exit...no need to initialize all of the audio
1364 // stuff to display the version string.
1365 std::shared_ptr< wxCmdLineParser > parser{ ParseCommandLine() };
1366 if (!parser)
1367 {
1368 // Either user requested help or a parsing error occurred
1369 exit(1);
1370 }
1371
1372 wxString journalFileName;
1373 const bool playingJournal = parser->Found("j", &journalFileName);
1374
1375#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
1376 if (!playingJournal)
1377 this->AssociateFileTypes();
1378#endif
1379
1380 if (parser->Found(wxT("v")))
1381 {
1382 wxPrintf("Audacity v%s\n", AUDACITY_VERSION_STRING);
1383 exit(0);
1384 }
1385
1386 long lval;
1387 if (parser->Found(wxT("b"), &lval))
1388 {
1389 if (lval < 256 || lval > 100000000)
1390 {
1391 wxPrintf(_("Block size must be within 256 to 100000000\n"));
1392 exit(1);
1393 }
1394
1396 }
1397
1398 if (playingJournal)
1399 Journal::SetInputFileName( journalFileName );
1400
1401 // BG: Create a temporary window to set as the top window
1402 wxImage logoimage((const char **)AudacityLogoWithName_xpm);
1403 logoimage.Rescale(logoimage.GetWidth() / 2, logoimage.GetHeight() / 2);
1404 if( GetLayoutDirection() == wxLayout_RightToLeft)
1405 logoimage = logoimage.Mirror();
1406 wxBitmap logo(logoimage);
1407
1408 AudacityProject *project;
1409 {
1410 // Bug 718: Position splash screen on same screen
1411 // as where Audacity project will appear.
1412 wxRect wndRect;
1413 bool bMaximized = false;
1414 bool bIconized = false;
1415 GetNextWindowPlacement(&wndRect, &bMaximized, &bIconized);
1416
1417 wxSplashScreen temporarywindow(
1418 logo,
1419 wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_NO_TIMEOUT,
1420 0,
1421 NULL,
1422 wxID_ANY,
1423 wndRect.GetTopLeft(),
1424 wxDefaultSize,
1425 wxSTAY_ON_TOP);
1426
1427 // Unfortunately with the Windows 10 Creators update, the splash screen
1428 // now appears before setting its position.
1429 // On a dual monitor screen it will appear on one screen and then
1430 // possibly jump to the second.
1431 // We could fix this by writing our own splash screen and using Hide()
1432 // until the splash scren was correctly positioned, then Show()
1433
1434 // Possibly move it on to the second screen...
1435 temporarywindow.SetPosition( wndRect.GetTopLeft() );
1436 // Centered on whichever screen it is on.
1437 temporarywindow.Center();
1438 temporarywindow.SetTitle(_("Audacity is starting up..."));
1439 SetTopWindow(&temporarywindow);
1440 temporarywindow.Raise();
1441
1442 // ANSWER-ME: Why is YieldFor needed at all?
1443 //wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI|wxEVT_CATEGORY_USER_INPUT|wxEVT_CATEGORY_UNKNOWN);
1444 wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);
1445
1446 //JKC: Would like to put module loading here.
1447
1448 // More initialization
1449
1450 InitDitherers();
1451 AudioIO::Init();
1452
1453#ifdef __WXMAC__
1454
1455 // On the Mac, users don't expect a program to quit when you close the last window.
1456 // Create a menubar that will show when all project windows are closed.
1457
1458 auto fileMenu = std::make_unique<wxMenu>();
1459 auto urecentMenu = std::make_unique<wxMenu>();
1460 auto recentMenu = urecentMenu.get();
1461 fileMenu->Append(wxID_NEW, wxString(_("&New")) + wxT("\tCtrl+N"));
1462 fileMenu->Append(wxID_OPEN, wxString(_("&Open...")) + wxT("\tCtrl+O"));
1463 fileMenu->AppendSubMenu(urecentMenu.release(), _("Open &Recent..."));
1464 fileMenu->Append(wxID_ABOUT, _("&About Audacity..."));
1465 fileMenu->Append(wxID_PREFERENCES, wxString(_("&Preferences...")) + wxT("\tCtrl+,"));
1466
1467 {
1468 auto menuBar = std::make_unique<wxMenuBar>();
1469 menuBar->Append(fileMenu.release(), _("&File"));
1470
1471 // PRL: Are we sure wxWindows will not leak this menuBar?
1472 // The online documentation is not explicit.
1473 wxMenuBar::MacSetCommonMenuBar(menuBar.release());
1474 }
1475
1476 auto &recentFiles = FileHistory::Global();
1477 recentFiles.UseMenu(recentMenu);
1478
1479#endif //__WXMAC__
1480 temporarywindow.Show(false);
1481 }
1482
1483 //Search for the new plugins
1484 std::vector<wxString> failedPlugins;
1485 if(!playingJournal)
1486 {
1487 auto newPlugins = PluginManager::Get().CheckPluginUpdates();
1488 if(!newPlugins.empty())
1489 {
1490 PluginStartupRegistration reg(newPlugins);
1491 reg.Run();
1492 failedPlugins = reg.GetFailedPluginsPaths();
1493 }
1494 }
1495
1496 // Must do this before creating the first project, else the early exit path
1497 // may crash
1499 return false;
1500
1501 // Workaround Bug 1377 - Crash after Audacity starts and low disk space warning appears
1502 // The temporary splash window is closed AND cleaned up, before attempting to create
1503 // a project and possibly creating a modal warning dialog by doing so.
1504 // Also fixes problem of warning being obscured.
1505 // Downside is that we have no splash screen for the (brief) time that we spend
1506 // creating the project.
1507 // Root cause is problem with wxSplashScreen and other dialogs co-existing, that
1508 // seemed to arrive with wx3.
1509 {
1510 project = ProjectManager::New();
1511 }
1512
1513 if (!playingJournal && ProjectSettings::Get(*project).GetShowSplashScreen())
1514 {
1515 // This may do a check-for-updates at every start up.
1516 // Mainly this is to tell users of ALPHAS who don't know that they have an ALPHA.
1517 // Disabled for now, after discussion.
1518 // project->MayCheckForUpdates();
1520 }
1521
1522#if defined(HAVE_UPDATES_CHECK)
1523 UpdateManager::Start(playingJournal);
1524#endif
1525
1526 #ifdef USE_FFMPEG
1527 FFmpegStartup();
1528 #endif
1529
1531
1532 // Bug1561: delay the recovery dialog, to avoid crashes.
1533 CallAfter( [=] () mutable {
1534 // Remove duplicate shortcuts when there's a change of version
1535 int vMajorInit, vMinorInit, vMicroInit;
1536 gPrefs->GetVersionKeysInit(vMajorInit, vMinorInit, vMicroInit);
1537 if (vMajorInit != AUDACITY_VERSION || vMinorInit != AUDACITY_RELEASE
1538 || vMicroInit != AUDACITY_REVISION) {
1540 }
1541 //
1542 // Auto-recovery
1543 //
1544 bool didRecoverAnything = false;
1545 // This call may reassign project (passed by reference)
1546 if (!playingJournal)
1547 {
1548 if (!ShowAutoRecoveryDialogIfNeeded(project, &didRecoverAnything))
1549 {
1550 QuitAudacity(true);
1551 }
1552 }
1553
1554 //
1555 // Remainder of command line parsing, but only if we didn't recover
1556 //
1557 if (project && !didRecoverAnything)
1558 {
1559 if (parser->Found(wxT("t")))
1560 {
1561 RunBenchmark( nullptr, *project);
1562 QuitAudacity(true);
1563 }
1564
1565 for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++)
1566 {
1567 // PRL: Catch any exceptions, don't try this file again, continue to
1568 // other files.
1569 SafeMRUOpen(parser->GetParam(i));
1570 }
1571
1572 if(!failedPlugins.empty())
1573 {
1574 auto dialog = safenew IncompatiblePluginsDialog(GetTopWindow(), wxID_ANY, ScanType::Startup, failedPlugins);
1575 dialog->Bind(wxEVT_CLOSE_WINDOW, [dialog](wxCloseEvent&) { dialog->Destroy(); });
1576 dialog->Show();
1577 }
1578 }
1579 } );
1580
1581 gInited = true;
1582
1584
1585 mTimer.SetOwner(this, kAudacityAppTimerID);
1586 mTimer.Start(200);
1587
1588#ifdef EXPERIMENTAL_EASY_CHANGE_KEY_BINDINGS
1589 static CommandManager::GlobalMenuHook::Scope scope{
1590 [](const CommandID &id){
1591 if (::wxGetMouseState().ShiftDown()) {
1592 // Only want one page of the preferences
1593 PrefsPanel::Factories factories;
1594 factories.push_back(KeyConfigPrefsFactory( id ));
1595 const auto pProject = GetActiveProject().lock();
1596 auto pWindow = FindProjectFrame( pProject.get() );
1597 // pProject may be null
1598 GlobalPrefsDialog dialog( pWindow, pProject.get(), factories );
1599 dialog.ShowModal();
1601 return true;
1602 }
1603 else
1604 return false;
1605 } };
1606#endif
1607
1608#if defined(__WXMAC__)
1609 // The first time this version of Audacity is run or when the preferences
1610 // are reset, execute the "tccutil" command to reset the microphone permissions
1611 // currently assigned to Audacity. The end result is that the user will be
1612 // prompted to approve/deny Audacity access (again).
1613 //
1614 // This should resolve confusion of why Audacity appears to record, but only
1615 // gets silence due to Audacity being denied microphone access previously.
1616 bool permsReset = false;
1617 gPrefs->Read(wxT("/MicrophonePermissionsReset"), &permsReset, false);
1618 if (!permsReset) {
1619 system("tccutil reset Microphone org.audacityteam.audacity");
1620 gPrefs->Write(wxT("/MicrophonePermissionsReset"), true);
1621 }
1622#endif
1623
1624#if defined(__WXMAC__)
1625 // Bug 2709: Workaround CoreSVG locale issue
1626 Bind(wxEVT_MENU_OPEN, [=](wxMenuEvent &event)
1627 {
1628 wxSetlocale(LC_NUMERIC, wxString(wxT("C")));
1629 event.Skip();
1630 });
1631
1632 Bind(wxEVT_MENU_CLOSE, [=](wxMenuEvent &event)
1633 {
1634 wxSetlocale(LC_NUMERIC, Languages::GetLocaleName());
1635 event.Skip();
1636 });
1637#endif
1638
1639#ifdef HAS_CUSTOM_URL_HANDLING
1640 // Schemes are case insensitive as per RFC: https://www.rfc-editor.org/rfc/rfc3986#section-3.1
1641 URLSchemesRegistry::Get().RegisterScheme(AUDACITY_NAME);
1642
1643 wxString url;
1644 if (parser->Found("u", &url))
1645 {
1646 auto utf8Url = url.ToUTF8();
1647 URLSchemesRegistry::Get().HandleURL({ utf8Url.data(), utf8Url.length() });
1648 }
1649#endif
1650
1651 return TRUE;
1652}
1653
1655{
1656 // Returns 0 to the command line if the run completed normally
1657 auto result = wxApp::OnRun();
1658 if (result == 0)
1659 // If not otherwise abnormal, report any journal sync failure
1660 result = Journal::GetExitCode();
1661 return result;
1662}
1663
1664void AudacityApp::OnIdle( wxIdleEvent &evt )
1665{
1666 evt.Skip();
1667 try {
1668 if ( Journal::Dispatch() )
1669 evt.RequestMore();
1670 }
1671 catch( ... ) {
1672 // Hmm, wxWidgets doesn't guard calls to the idle handler as for other
1673 // events. So replicate some of the try-catch logic here.
1675 // Fall through and return, allowing delayed handler action of
1676 // AudacityException to clean up
1677 }
1678}
1679
1681{
1682 mCmdHandler = std::make_unique<CommandHandler>();
1683 //SetNextHandler(mCmdHandler);
1684}
1685
1686// AppCommandEvent callback - just pass the event on to the CommandHandler
1688{
1689 wxASSERT(NULL != mCmdHandler);
1690 mCmdHandler->OnReceiveCommand(event);
1691}
1692
1693void AudacityApp::OnKeyDown(wxKeyEvent &event)
1694{
1695 if(event.GetKeyCode() == WXK_ESCAPE) {
1696 // Stop play, including scrub, but not record
1697 if ( auto project = ::GetActiveProject().lock() ) {
1698 auto token = ProjectAudioIO::Get( *project ).GetAudioIOToken();
1699 auto &scrubber = Scrubber::Get( *project );
1700 auto scrubbing = scrubber.HasMark();
1701 if (scrubbing)
1702 scrubber.Cancel();
1703 auto gAudioIO = AudioIO::Get();
1704 if((token > 0 &&
1705 gAudioIO->IsAudioTokenActive(token) &&
1706 gAudioIO->GetNumCaptureChannels() == 0) ||
1707 scrubbing)
1708 // ESC out of other play (but not record)
1709 ProjectAudioManager::Get( *project ).Stop();
1710 else
1711 event.Skip();
1712 }
1713 }
1714
1715 event.Skip();
1716}
1717
1718// Ensures directory is created and puts the name into result.
1719// result is unchanged if unsuccessful.
1720void SetToExtantDirectory( wxString & result, const wxString & dir ){
1721 // don't allow path of "".
1722 if( dir.empty() )
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 auto tempFromPrefs = TempDirectory::TempDir();
1738 auto tempDefaultLoc = TempDirectory::DefaultTempDir();
1739
1740 wxString temp;
1741
1742 #ifdef __WXGTK__
1743 if (tempFromPrefs.length() > 0 && tempFromPrefs[0] != wxT('/'))
1744 tempFromPrefs = wxT("");
1745 #endif
1746
1747 // Stop wxWidgets from printing its own error messages
1748
1749 wxLogNull logNo;
1750
1751 // Try temp dir that was stored in prefs first
1752 if( TempDirectory::IsTempDirectoryNameOK( tempFromPrefs ) )
1753 SetToExtantDirectory( temp, tempFromPrefs );
1754
1755 // If that didn't work, try the default location
1756
1757 if (temp.empty())
1758 SetToExtantDirectory( temp, tempDefaultLoc );
1759
1760 // Check temp directory ownership on *nix systems only
1761 #ifdef __UNIX__
1762 struct stat tempStatBuf;
1763 if ( lstat(temp.mb_str(), &tempStatBuf) != 0 ) {
1764 temp.clear();
1765 }
1766 else {
1767 if ( geteuid() != tempStatBuf.st_uid ) {
1768 temp.clear();
1769 }
1770 }
1771 #endif
1772
1773 if (temp.empty()) {
1774 // Failed
1775 if( !TempDirectory::IsTempDirectoryNameOK( tempFromPrefs ) ) {
1777"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 {
1780"Audacity could not find a place to store temporary files.\nPlease enter an appropriate directory in the preferences dialog."));
1781 }
1782
1783 // Only want one page of the preferences
1784 PrefsPanel::Factories factories;
1785 factories.push_back(DirectoriesPrefsFactory());
1786 GlobalPrefsDialog dialog(nullptr, nullptr, factories);
1787 dialog.ShowModal();
1788
1790"Audacity is now going to exit. Please launch Audacity again to use the new temporary directory."));
1791 return false;
1792 }
1793
1794 // The permissions don't always seem to be set on
1795 // some platforms. Hopefully this fixes it...
1796 #ifdef __UNIX__
1797 chmod(OSFILENAME(temp), 0700);
1798 #endif
1799
1801 FileNames::UpdateDefaultPath(FileNames::Operation::Temp, temp);
1802
1803 return true;
1804}
1805
1806#if defined(__WXMSW__)
1807
1808// Return true if there are no other instances of Audacity running,
1809// false otherwise.
1810
1812{
1813 wxString name = wxString::Format(wxT("audacity-lock-%s"), wxGetUserId());
1814 mChecker.reset();
1815 auto checker = std::make_unique<wxSingleInstanceChecker>();
1816
1817 auto runningTwoCopiesStr = XO("Running two copies of Audacity simultaneously may cause\ndata loss or cause your system to crash.\n\n");
1818
1819 if (!checker->Create(name, dir))
1820 {
1821 // Error initializing the wxSingleInstanceChecker. We don't know
1822 // whether there is another instance running or not.
1823
1824 auto prompt = XO(
1825"Audacity was not able to lock the temporary files directory.\nThis folder may be in use by another copy of Audacity.\n")
1826 + runningTwoCopiesStr
1827 + XO("Do you still want to start Audacity?");
1828 int action = AudacityMessageBox(
1829 prompt,
1830 XO("Error Locking Temporary Folder"),
1831 wxYES_NO | wxICON_EXCLAMATION, NULL);
1832 if (action == wxNO)
1833 return false;
1834 }
1835 else if ( checker->IsAnotherRunning() ) {
1836 // Parse the command line to ensure correct syntax, but
1837 // ignore options other than -v, and only use the filenames, if any.
1838 auto parser = ParseCommandLine();
1839 if (!parser)
1840 {
1841 // Complaints have already been made
1842 return false;
1843 }
1844
1845 if (parser->Found(wxT("v")))
1846 {
1847 wxPrintf("Audacity v%s\n", AUDACITY_VERSION_STRING);
1848 return false;
1849 }
1850
1851 // Windows and Linux require absolute file names as command may
1852 // not come from current working directory.
1853 FilePaths filenames;
1854 for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++)
1855 {
1856 wxFileName filename(parser->GetParam(i));
1857 if (filename.MakeAbsolute())
1858 {
1859 filenames.push_back(filename.GetLongPath());
1860 }
1861 }
1862
1863 #ifdef HAS_CUSTOM_URL_HANDLING
1864 wxString url;
1865 parser->Found("u", &url);
1866 #endif
1867
1868
1869 // On Windows, we attempt to make a connection
1870 // to an already active Audacity. If successful, we send
1871 // the first command line argument (the audio file name)
1872 // to that Audacity for processing.
1873 wxClient client;
1874
1875 // We try up to 50 times since there's a small window
1876 // where the server may not have been fully initialized.
1877 for (int i = 0; i < 50; i++)
1878 {
1879 std::unique_ptr<wxConnectionBase> conn{ client.MakeConnection(wxEmptyString, IPC_APPL, IPC_TOPIC) };
1880 if (conn)
1881 {
1882 bool ok = false;
1883 #ifdef HAS_CUSTOM_URL_HANDLING
1884 if (!url.empty())
1885 {
1886 if (!conn->Execute(urlPrefix + url))
1887 return false;
1888 }
1889 #endif
1890
1891 if (filenames.size() > 0)
1892 {
1893 for (size_t i = 0, cnt = filenames.size(); i < cnt; i++)
1894 {
1895 ok = conn->Execute(filenames[i]);
1896 }
1897 }
1898 else
1899 {
1900 // Send an empty string to force existing Audacity to front
1901 ok = conn->Execute(wxEmptyString);
1902 }
1903
1904 if (ok)
1905 return false;
1906 }
1907
1908 using namespace std::chrono;
1909 std::this_thread::sleep_for(10ms);
1910 }
1911 // There is another copy of Audacity running. Force quit.
1912
1913 auto prompt = XO(
1914"The system has detected that another copy of Audacity is running.\n")
1915 + runningTwoCopiesStr
1916 + XO(
1917"Use the New or Open commands in the currently running Audacity\nprocess to open multiple projects simultaneously.\n");
1919 prompt, XO("Audacity is already running"),
1920 wxOK | wxICON_ERROR);
1921
1922 return false;
1923 }
1924
1925 // Create the DDE IPC server
1926 mIPCServ = std::make_unique<IPCServ>(IPC_APPL);
1927 mChecker = std::move(checker);
1928 return true;
1929}
1930#endif
1931
1932#if defined(__UNIX__)
1933
1934#include <sys/ipc.h>
1935#include <sys/sem.h>
1936#include <sys/shm.h>
1937
1938// Return true if there are no other instances of Audacity running,
1939// false otherwise.
1940
1941bool AudacityApp::CreateSingleInstanceChecker(const wxString &dir)
1942{
1943 mIPCServ.reset();
1944
1945 bool isServer = false;
1946 wxIPV4address addr;
1947 addr.LocalHost();
1948
1949 struct sembuf op = {};
1950
1951 // Generate the IPC key we'll use for both shared memory and semaphores.
1952 wxString datadir = FileNames::DataDir();
1953 key_t memkey = ftok(datadir.c_str(), 0);
1954 key_t servkey = ftok(datadir.c_str(), 1);
1955 key_t lockkey = ftok(datadir.c_str(), 2);
1956
1957 // Create and map the shared memory segment where the port number
1958 // will be stored.
1959 int memid = shmget(memkey, sizeof(int), IPC_CREAT | S_IRUSR | S_IWUSR);
1960 if (memid == -1)
1961 {
1963 XO("Unable to create shared memory segment.\n\n"
1964 "error code=%d : \"%s\".").Format(errno, strerror(errno)),
1965 XO("Audacity Startup Failure"),
1966 wxOK | wxICON_ERROR);
1967
1968 return false;
1969 }
1970
1971 int *portnum = (int *) shmat(memid, nullptr, 0);
1972
1973 // Create (or return) the SERVER semaphore ID
1974 int servid = semget(servkey, 1, IPC_CREAT | S_IRUSR | S_IWUSR);
1975
1976 // Create the LOCK semaphore only if it doesn't already exist.
1977 int lockid = semget(lockkey, 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
1978
1979 // If the LOCK semaphore was successfully created, then this is the first
1980 // time Audacity has been run during this boot of the system. In this
1981 // case we know we'll become the "server" application, so set up the
1982 // semaphores to prepare for it.
1983 if (lockid != -1)
1984 {
1985 // Initialize value of each semaphore, 1 indicates released and 0
1986 // indicates acquired.
1987 //
1988 // Note that this action is NOT recorded in the semaphore's
1989 // UNDO buffer.
1990 semctl(servid, 0, SETVAL, 1);
1991 semctl(lockid, 0, SETVAL, 1);
1992
1993 // Now acquire them so the semaphores will be set to the
1994 // released state when the process terminates.
1995 op.sem_num = 0;
1996 op.sem_op = -1;
1997 op.sem_flg = SEM_UNDO;
1998 if (semop(lockid, &op, 1) == -1 || semop(servid, &op, 1) == -1)
1999 {
2001 XO("Unable to acquire semaphores.\n\n"
2002 "This is likely due to a resource shortage\n"
2003 "and a reboot may be required."),
2004 XO("Audacity Startup Failure"),
2005 wxOK | wxICON_ERROR);
2006
2007 return false;
2008 }
2009
2010 // We will be the server...
2011 isServer = true;
2012 }
2013 // Something catastrophic must have happened, so bail.
2014 else if (errno != EEXIST)
2015 {
2017 XO("Unable to create semaphores.\n\n"
2018 "This is likely due to a resource shortage\n"
2019 "and a reboot may be required."),
2020 XO("Audacity Startup Failure"),
2021 wxOK | wxICON_ERROR);
2022
2023 return false;
2024 }
2025 // Otherwise it's a normal startup and we need to determine whether
2026 // we'll be the server or the client.
2027 else
2028 {
2029 // Retrieve the LOCK semaphore since we wouldn't have gotten it above.
2030 lockid = semget(lockkey, 1, 0);
2031
2032 // Acquire the LOCK semaphore. We may block here if another
2033 // process is currently setting up the server.
2034 op.sem_num = 0;
2035 op.sem_op = -1;
2036 op.sem_flg = SEM_UNDO;
2037 if (semop(lockid, &op, 1) == -1)
2038 {
2040 XO("Unable to acquire lock semaphore.\n\n"
2041 "This is likely due to a resource shortage\n"
2042 "and a reboot may be required."),
2043 XO("Audacity Startup Failure"),
2044 wxOK | wxICON_ERROR);
2045
2046 return false;
2047 }
2048
2049 // Try to acquire the SERVER semaphore. If it's not currently active, then
2050 // we will become the server. Otherwise, this will fail and we'll know that
2051 // the server is already active and we will become the client.
2052 op.sem_num = 0;
2053 op.sem_op = -1;
2054 op.sem_flg = IPC_NOWAIT | SEM_UNDO;
2055 if (semop(servid, &op, 1) == 0)
2056 {
2057 isServer = true;
2058 }
2059 else if (errno != EAGAIN)
2060 {
2062 XO("Unable to acquire server semaphore.\n\n"
2063 "This is likely due to a resource shortage\n"
2064 "and a reboot may be required."),
2065 XO("Audacity Startup Failure"),
2066 wxOK | wxICON_ERROR);
2067
2068 return false;
2069 }
2070 }
2071
2072 // Initialize the socket server if we're to be the server.
2073 if (isServer)
2074 {
2075 // The system will randomly assign a port
2076 addr.Service(0);
2077
2078 // Create the socket and bind to it.
2079 auto serv = std::make_unique<wxSocketServer>(addr, wxSOCKET_NOWAIT);
2080 if (serv && serv->IsOk())
2081 {
2082 serv->SetEventHandler(*this, ID_IPC_SERVER);
2083 serv->SetNotify(wxSOCKET_CONNECTION_FLAG);
2084 serv->Notify(true);
2085 mIPCServ = std::move(serv);
2086
2087 // Save the port number in shared memory so that clients
2088 // know where to connect.
2089 mIPCServ->GetLocal(addr);
2090 *portnum = addr.Service();
2091 }
2092
2093 // Now that the server is active, we release the LOCK semaphore
2094 // to allow any waiters to continue. The SERVER semaphore will
2095 // remain locked for the duration of this processes execution
2096 // and will be cleaned up by the system.
2097 op.sem_num = 0;
2098 op.sem_op = 1;
2099 semop(lockid, &op, 1);
2100
2101 // Bail if the server creation failed.
2102 if (mIPCServ == nullptr)
2103 {
2105 XO("The Audacity IPC server failed to initialize.\n\n"
2106 "This is likely due to a resource shortage\n"
2107 "and a reboot may be required."),
2108 XO("Audacity Startup Failure"),
2109 wxOK | wxICON_ERROR);
2110
2111 return false;
2112 }
2113
2114 // We've successfully created the socket server and the app
2115 // should continue to initialize.
2116 return true;
2117 }
2118
2119 // Retrieve the port number that the server is listening on.
2120 addr.Service(*portnum);
2121
2122 // Now release the LOCK semaphore.
2123 op.sem_num = 0;
2124 op.sem_op = 1;
2125 semop(lockid, &op, 1);
2126
2127 // If we get here, then Audacity is currently active. So, we connect
2128 // to it and we forward all filenames listed on the command line to
2129 // the active process.
2130
2131 // Setup the socket
2132 //
2133 // A wxSocketClient must not be deleted by us, but rather, let the
2134 // framework do appropriate delayed deletion after Destroy()
2135 Destroy_ptr<wxSocketClient> sock { safenew wxSocketClient() };
2136 sock->SetFlags(wxSOCKET_WAITALL);
2137
2138 // Attempt to connect to an active Audacity.
2139 sock->Connect(addr, true);
2140 if (!sock->IsConnected())
2141 {
2142 // All attempts to become the server or connect to one have failed. Not
2143 // sure what we can say about the error, but it's probably not because
2144 // Audacity is already running.
2146 XO("An unrecoverable error has occurred during startup"),
2147 XO("Audacity Startup Failure"),
2148 wxOK | wxICON_ERROR);
2149
2150 return false;
2151 }
2152
2153 // Parse the command line to ensure correct syntax, but ignore
2154 // options other than -v, and only use the filenames, if any.
2155 auto parser = ParseCommandLine();
2156 if (!parser)
2157 {
2158 // Complaints have already been made
2159 return false;
2160 }
2161
2162 // Display Audacity's version if requested
2163 if (parser->Found(wxT("v")))
2164 {
2165 wxPrintf("Audacity v%s\n", AUDACITY_VERSION_STRING);
2166
2167 return false;
2168 }
2169
2170#ifdef HAS_CUSTOM_URL_HANDLING
2171 wxString url;
2172
2173 if (parser->Found(wxT("u"), &url))
2174 {
2175 if (!url.empty())
2176 {
2177 url = urlPrefix + url;
2178 auto str = url.c_str().AsWChar();
2179
2180 sock->WriteMsg(str, (url.length() + 1) * sizeof(*str));
2181 }
2182 }
2183#endif
2184
2185#if defined(__WXMAC__)
2186 // On macOS the client gets events from the wxWidgets framework that
2187 // go to AudacityApp::MacOpenFile. Forward the file names to the prior
2188 // instance via the socket.
2189 for (const auto &filename: ofqueue)
2190 {
2191 auto str = filename.c_str().AsWChar();
2192 sock->WriteMsg(str, (filename.length() + 1) * sizeof(*str));
2193 }
2194#endif
2195
2196 // On macOS and Linux, forward any file names found in the command
2197 // line arguments.
2198 for (size_t j = 0, cnt = parser->GetParamCount(); j < cnt; ++j)
2199 {
2200 wxFileName filename(parser->GetParam(j));
2201 if (filename.MakeAbsolute())
2202 {
2203 const wxString param = filename.GetLongPath();
2204 sock->WriteMsg((const wxChar *) param, (param.length() + 1) * sizeof(wxChar));
2205 }
2206 }
2207
2208 // Send an empty string to force existing Audacity to front
2209 sock->WriteMsg(wxEmptyString, sizeof(wxChar));
2210
2211 // We've forwarded all of the filenames, so let the caller know
2212 // to terminate.
2213 return false;
2214}
2215
2216void AudacityApp::OnServerEvent(wxSocketEvent & /* evt */)
2217{
2218 wxSocketBase *sock;
2219
2220 // Accept all pending connection requests
2221 do
2222 {
2223 sock = mIPCServ->Accept(false);
2224 if (sock)
2225 {
2226 // Setup the socket
2227 sock->SetEventHandler(*this, ID_IPC_SOCKET);
2228 sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
2229 sock->Notify(true);
2230 }
2231 } while (sock);
2232}
2233
2234void AudacityApp::OnSocketEvent(wxSocketEvent & evt)
2235{
2236 wxSocketBase *sock = evt.GetSocket();
2237
2238 if (evt.GetSocketEvent() == wxSOCKET_LOST)
2239 {
2240 sock->Destroy();
2241 return;
2242 }
2243
2244 // Read the length of the filename and bail if we have a short read
2245 wxChar name[PATH_MAX];
2246 sock->ReadMsg(&name, sizeof(name));
2247 if (!sock->Error())
2248 {
2249 // Add the filename to the queue. It will be opened by
2250 // the OnTimer() event when it is safe to do so.
2251 ofqueue.push_back(name);
2252 }
2253}
2254
2255#endif
2256
2257std::unique_ptr<wxCmdLineParser> AudacityApp::ParseCommandLine()
2258{
2259 auto parser = std::make_unique<wxCmdLineParser>(argc, argv);
2260 if (!parser)
2261 {
2262 return nullptr;
2263 }
2264
2265 /*i18n-hint: This controls the number of bytes that Audacity will
2266 * use when writing files to the disk */
2267 parser->AddOption(wxT("b"), wxT("blocksize"), _("set max disk block size in bytes"),
2268 wxCMD_LINE_VAL_NUMBER);
2269
2270 const auto journalOptionDescription =
2271 /*i18n-hint: brief help message for Audacity's command-line options
2272 A journal contains a sequence of user interface interactions to be repeated
2273 "log," "trail," "trace" have somewhat similar meanings */
2274 _("replay a journal file");
2275
2276 parser->AddOption(wxT("j"), wxT("journal"), journalOptionDescription);
2277
2278 /*i18n-hint: This displays a list of available options */
2279 parser->AddSwitch(wxT("h"), wxT("help"), _("this help message"),
2280 wxCMD_LINE_OPTION_HELP);
2281
2282 /*i18n-hint: This runs a set of automatic tests on Audacity itself */
2283 parser->AddSwitch(wxT("t"), wxT("test"), _("run self diagnostics"));
2284
2285 /*i18n-hint: This displays the Audacity version */
2286 parser->AddSwitch(wxT("v"), wxT("version"), _("display Audacity version"));
2287
2288 /*i18n-hint: This is a list of one or more files that Audacity
2289 * should open upon startup */
2290 parser->AddParam(_("audio or project file name"),
2291 wxCMD_LINE_VAL_STRING,
2292 wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL);
2293
2294#ifdef HAS_CUSTOM_URL_HANDLING
2295 /* i18n-hint: This option is used to handle custom URLs in Audacity */
2296 parser->AddOption(wxT("u"), wxT("url"), _("Handle 'audacity://' url"));
2297#endif
2298
2299 // Run the parser
2300 if (parser->Parse() == 0)
2301 return parser;
2302
2303 return{};
2304}
2305
2306void AudacityApp::OnQueryEndSession(wxCloseEvent & event)
2307{
2308 bool mustVeto = false;
2309
2310#ifdef __WXMAC__
2311 mustVeto = wxDialog::OSXHasModalDialogsOpen();
2312#endif
2313
2314 if ( mustVeto )
2315 event.Veto(true);
2316 else
2317 OnEndSession(event);
2318}
2319
2320void AudacityApp::OnEndSession(wxCloseEvent & event)
2321{
2322 bool force = !event.CanVeto();
2323
2324 // Try to close each open window. If the user hits Cancel
2325 // in a Save Changes dialog, don't continue.
2326 gIsQuitting = true;
2327 if (AllProjects{}.size())
2328 // PRL: Always did at least once before close might be vetoed
2329 // though I don't know why that is important
2331 bool closedAll = CloseAllProjects( force );
2332 if ( !closedAll )
2333 {
2334 gIsQuitting = false;
2335 event.Veto();
2336 }
2337}
2338
2340{
2341 gIsQuitting = true;
2342 while(Pending())
2343 {
2344 Dispatch();
2345 }
2346
2348
2349 if(gPrefs)
2350 {
2351 bool bFalse = false;
2352 //Should we change the commands.cfg location next startup?
2353 if(gPrefs->Read(wxT("/QDeleteCmdCfgLocation"), &bFalse))
2354 {
2355 gPrefs->DeleteEntry(wxT("/QDeleteCmdCfgLocation"));
2356 gPrefs->Write(wxT("/DeleteCmdCfgLocation"), true);
2357 gPrefs->Flush();
2358 }
2359 }
2360
2362
2364
2365 DeinitFFT();
2366
2367#ifdef HAS_NETWORKING
2369#endif
2370
2372
2374
2375 // Terminate the PluginManager (must be done before deleting the locale)
2377
2378 return 0;
2379}
2380
2381// The following five methods are currently only used on Mac OS,
2382// where it's possible to have a menu bar but no windows open.
2383// It doesn't hurt any other platforms, though.
2384
2385// ...That is, as long as you check to see if no windows are open
2386// before executing the stuff.
2387// To fix this, check to see how many project windows are open,
2388// and skip the event unless none are open (which should only happen
2389// on the Mac, at least currently.)
2390
2391void AudacityApp::OnMenuAbout(wxCommandEvent & /*event*/)
2392{
2393 // This function shadows a similar function
2394 // in Menus.cpp, but should only be used on the Mac platform.
2395#ifdef __WXMAC__
2396 // Modeless dialog, consistent with other Mac applications
2397 // Not more than one at once!
2398 const auto instance = AboutDialog::ActiveIntance();
2399 if (instance)
2400 instance->Raise();
2401 else
2402 // This dialog deletes itself when dismissed
2403 (safenew AboutDialog{ nullptr })->Show(true);
2404#else
2405 wxASSERT(false);
2406#endif
2407}
2408
2409void AudacityApp::OnMenuNew(wxCommandEvent & event)
2410{
2411 // This function shadows a similar function
2412 // in Menus.cpp, but should only be used on the Mac platform
2413 // when no project windows are open. This check assures that
2414 // this happens, and enable the same code to be present on
2415 // all platforms.
2416
2417 if(AllProjects{}.empty())
2418 (void) ProjectManager::New();
2419 else
2420 event.Skip();
2421}
2422
2423
2424void AudacityApp::OnMenuOpen(wxCommandEvent & event)
2425{
2426 // This function shadows a similar function
2427 // in Menus.cpp, but should only be used on the Mac platform
2428 // when no project windows are open. This check assures that
2429 // this happens, and enable the same code to be present on
2430 // all platforms.
2431
2432
2433 if(AllProjects{}.empty())
2435 else
2436 event.Skip();
2437
2438
2439}
2440
2441void AudacityApp::OnMenuPreferences(wxCommandEvent & event)
2442{
2443 // This function shadows a similar function
2444 // in Menus.cpp, but should only be used on the Mac platform
2445 // when no project windows are open. This check assures that
2446 // this happens, and enable the same code to be present on
2447 // all platforms.
2448
2449 if(AllProjects{}.empty()) {
2450 GlobalPrefsDialog dialog(nullptr /* parent */, nullptr );
2451 dialog.ShowModal();
2452 }
2453 else
2454 event.Skip();
2455
2456}
2457
2458void AudacityApp::OnMenuExit(wxCommandEvent & event)
2459{
2460 // This function shadows a similar function
2461 // in Menus.cpp, but should only be used on the Mac platform
2462 // when no project windows are open. This check assures that
2463 // this happens, and enable the same code to be present on
2464 // all platforms.
2465
2466 // LL: Removed "if" to allow closing based on final project count.
2467 // if(AllProjects{}.empty())
2468 QuitAudacity();
2469
2470 // LL: Veto quit if projects are still open. This can happen
2471 // if the user selected Cancel in a Save dialog.
2472 event.Skip(AllProjects{}.empty());
2473
2474}
2475
2476#ifndef __WXMAC__
2478{
2479 // Currently this is implemented only on macOS
2480}
2481#endif
2482
2483//BG: On Windows, associate the aup file type with Audacity
2484/* We do this in the Windows installer now,
2485 to avoid issues where user doesn't have admin privileges, but
2486 in case that didn't work, allow the user to decide at startup.
2487
2488 //v Should encapsulate this & allow access from Prefs, too,
2489 // if people want to manually change associations.
2490*/
2491#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
2493{
2494 // Check pref in case user has already decided against it.
2495 bool bWantAssociateFiles = true;
2496 if (gPrefs->Read(wxT("/WantAssociateFiles"), &bWantAssociateFiles) &&
2497 !bWantAssociateFiles)
2498 {
2499 // User has already decided against it
2500 return;
2501 }
2502
2503 wxRegKey associateFileTypes;
2504
2505 auto IsDefined = [&](const wxString &type)
2506 {
2507 associateFileTypes.SetName(wxString::Format(wxT("HKCR\\%s"), type));
2508 bool bKeyExists = associateFileTypes.Exists();
2509 if (!bKeyExists)
2510 {
2511 // Not at HKEY_CLASSES_ROOT. Try HKEY_CURRENT_USER.
2512 associateFileTypes.SetName(wxString::Format(wxT("HKCU\\Software\\Classes\\%s"), type));
2513 bKeyExists = associateFileTypes.Exists();
2514 }
2515 return bKeyExists;
2516 };
2517
2518 auto DefineType = [&](const wxString &type)
2519 {
2520 wxString root_key = wxT("HKCU\\Software\\Classes\\");
2521
2522 // Start with HKEY_CLASSES_CURRENT_USER.
2523 associateFileTypes.SetName(wxString::Format(wxT("%s%s"), root_key, type));
2524 if (!associateFileTypes.Create(true))
2525 {
2526 // Not at HKEY_CLASSES_CURRENT_USER. Try HKEY_CURRENT_ROOT.
2527 root_key = wxT("HKCR\\");
2528 associateFileTypes.SetName(wxString::Format(wxT("%s%s"), root_key, type));
2529 if (!associateFileTypes.Create(true))
2530 {
2531 // Actually, can't create keys. Empty root_key to flag failure.
2532 root_key.empty();
2533 }
2534 }
2535
2536 if (!root_key.empty())
2537 {
2538 associateFileTypes = wxT("Audacity.Project"); // Finally set value for the key
2539 }
2540
2541 return root_key;
2542 };
2543
2544 // Check for legacy and UP types
2545 if (IsDefined(wxT(".aup3")) && IsDefined(wxT(".aup")) && IsDefined(wxT("Audacity.Project")))
2546 {
2547 // Already defined, so bail
2548 return;
2549 }
2550
2551 // File types are not currently associated.
2552 int wantAssoc =
2554 XO(
2555"Audacity project (.aup3) files are not currently \nassociated with Audacity. \n\nAssociate them, so they open on double-click?"),
2556 XO("Audacity Project Files"),
2557 wxYES_NO | wxICON_QUESTION);
2558
2559 if (wantAssoc == wxNO)
2560 {
2561 // User said no. Set a pref so we don't keep asking.
2562 gPrefs->Write(wxT("/WantAssociateFiles"), false);
2563 gPrefs->Flush();
2564 return;
2565 }
2566
2567 // Show that user wants associations
2568 gPrefs->Write(wxT("/WantAssociateFiles"), true);
2569 gPrefs->Flush();
2570
2571 wxString root_key;
2572
2573 root_key = DefineType(wxT(".aup3"));
2574 if (root_key.empty())
2575 {
2576 //v Warn that we can't set keys. Ask whether to set pref for no retry?
2577 }
2578 else
2579 {
2580 DefineType(wxT(".aup"));
2581
2582 associateFileTypes = wxT("Audacity.Project"); // Finally set value for .AUP key
2583 associateFileTypes.SetName(root_key + wxT("Audacity.Project"));
2584 if (!associateFileTypes.Exists())
2585 {
2586 associateFileTypes.Create(true);
2587 associateFileTypes = wxT("Audacity Project File");
2588 }
2589
2590 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell"));
2591 if (!associateFileTypes.Exists())
2592 {
2593 associateFileTypes.Create(true);
2594 associateFileTypes = wxT("");
2595 }
2596
2597 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open"));
2598 if (!associateFileTypes.Exists())
2599 {
2600 associateFileTypes.Create(true);
2601 }
2602
2603 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\command"));
2604 wxString tmpRegAudPath;
2605 if(associateFileTypes.Exists())
2606 {
2607 tmpRegAudPath = associateFileTypes.QueryDefaultValue().Lower();
2608 }
2609
2610 if (!associateFileTypes.Exists() ||
2611 (tmpRegAudPath.Find(wxT("audacity.exe")) >= 0))
2612 {
2613 associateFileTypes.Create(true);
2614 associateFileTypes = (wxString)argv[0] + (wxString)wxT(" \"%1\"");
2615 }
2616
2617#if 0
2618 // These can be use later to support more startup messages
2619 // like maybe "Import into existing project" or some such.
2620 // Leaving here for an example...
2621 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec"));
2622 if (!associateFileTypes.Exists())
2623 {
2624 associateFileTypes.Create(true);
2625 associateFileTypes = wxT("%1");
2626 }
2627
2628 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Application"));
2629 if (!associateFileTypes.Exists())
2630 {
2631 associateFileTypes.Create(true);
2632 associateFileTypes = IPC_APPL;
2633 }
2634
2635 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Topic"));
2636 if (!associateFileTypes.Exists())
2637 {
2638 associateFileTypes.Create(true);
2639 associateFileTypes = IPC_TOPIC;
2640 }
2641#endif
2642 }
2643}
2644#endif
2645
AUDACITY_DLL_API std::weak_ptr< AudacityProject > GetActiveProject()
Handle changing of active project and keep global project pointer.
EVT_MENU(OnSetPlayRegionToSelectionID, AdornedRulerPanel::OnSetPlayRegionToSelection) EVT_COMMAND(OnTogglePinnedStateID
wxImage(22, 22)
wxT("CloseDown"))
Headers and event table macros for AppCommandEvent.
#define EVT_APP_COMMAND(winid, fn)
void SetToExtantDirectory(wxString &result, const wxString &dir)
int main(int argc, char *argv[])
static bool gIsQuitting
#define WL(lang, sublang)
EVT_MENU_RANGE(FileHistory::ID_RECENT_FIRST, FileHistory::ID_RECENT_LAST, AudacityApp::OnMRUFile) bool AudacityApp
#define ID_IPC_SERVER
#define kAudacityAppTimerID
static void QuitAudacity(bool bForce)
#define ID_IPC_SOCKET
static bool CloseAllProjects(bool force)
static wxArrayString ofqueue
static bool gInited
#define IPC_APPL
#define IPC_TOPIC
AudacityApp & wxGetApp()
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
bool ShowAutoRecoveryDialogIfNeeded(AudacityProject *&pproj, bool *didRecoverAnything)
END_EVENT_TABLE()
void RunBenchmark(wxWindow *parent, AudacityProject &project)
Definition: Benchmark.cpp:95
Contains declarations for the CommandHandler class.
static const AudacityProject::AttachedObjects::RegisteredFactory key
#define str(a)
static TransactionScope::Factory::Scope scope
PrefsPanel::Factory DirectoriesPrefsFactory()
const TranslatableString name
Definition: Distortion.cpp:82
void DeinitFFT()
Definition: FFT.cpp:114
void FFmpegStartup()
Definition: FFmpeg.cpp:65
#define OSFILENAME(X)
#define XO(s)
Definition: Internat.h:31
#define _(s)
Definition: Internat.h:75
PrefsPanel::Factory KeyConfigPrefsFactory(const CommandID &name)
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:146
#define safenew
Definition: MemoryX.h:10
std::unique_ptr< T, Destroyer< T > > Destroy_ptr
a convenience for using Destroyer
Definition: MemoryX.h:162
const std::wstring AppName
This program's name.
@ AppInitialized
@ AppQuiting
Declare a class for performing HTTP requests.
void FinishPreferences()
Definition: Prefs.cpp:227
FileConfig * gPrefs
Definition: Prefs.cpp:71
void InitPreferences(std::unique_ptr< FileConfig > uPrefs)
Definition: Prefs.cpp:201
void ResetPreferences()
Call this to reset preferences to an (almost)-"new" default state.
Definition: Prefs.cpp:208
#define AUDACITY_PREFS_VERSION_STRING
Definition: Prefs.h:39
wxString FilePath
Definition: Project.h:20
void GetNextWindowPlacement(wxRect *nextRect, bool *pMaximized, bool *pIconized)
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null,...
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
accessors for certain important windows associated with each project
void InitDitherers()
void CloseScreenshotTools()
Definition: Screenshot.cpp:150
THEME_API Theme theTheme
Definition: Theme.cpp:82
declares abstract base class Track, TrackList, and iterators over TrackList
Declare a class that handles managing of updates.
int id
static const auto fn
static void Init()
Definition: AColor.cpp:557
The AboutDialog shows the program version and developer credits.
Definition: AboutDialog.h:32
static AboutDialog * ActiveIntance()
size_t size() const
Definition: Project.cpp:17
const_iterator begin() const
Definition: Project.cpp:22
bool empty() const
Definition: Project.h:46
An event 'envelope' for sending Command objects through the wxwidgets event loop.
AudacityApp is the 'main' class for Audacity.
Definition: AudacityApp.h:37
void OnQueryEndSession(wxCloseEvent &event)
void OnMenuExit(wxCommandEvent &event)
void MacPrintFile(const wxString &fileName) override
wxTimer mTimer
Definition: AudacityApp.h:116
bool OSXIsGUIApplication() override
bool MRUOpen(const FilePath &fileName)
void OnKeyDown(wxKeyEvent &event)
void OnMenuPreferences(wxCommandEvent &event)
bool OnExceptionInMainLoop() override
void OnServerEvent(wxSocketEvent &evt)
bool InitPart2()
void OnEndSession(wxCloseEvent &event)
void MacNewFile() override
void OnTimer(wxTimerEvent &event)
bool Initialize(int &argc, wxChar **argv) override
std::unique_ptr< CommandHandler > mCmdHandler
Definition: AudacityApp.h:112
std::unique_ptr< wxSingleInstanceChecker > mChecker
Definition: AudacityApp.h:114
void MacFinishLaunching()
Observer::Subscription mThemeChangeSubscription
Definition: AudacityApp.h:110
void OnMRUFile(wxCommandEvent &event)
void AssociateFileTypes()
void OnMenuOpen(wxCommandEvent &event)
int OnRun() override
bool InitTempDir()
std::unique_ptr< wxCmdLineParser > ParseCommandLine()
void OnFatalException() override
void OnIdle(wxIdleEvent &)
void OnMRUClear(wxCommandEvent &event)
int OnExit(void) override
void OnMenuAbout(wxCommandEvent &event)
bool CreateSingleInstanceChecker(const wxString &dir)
void InitCommandHandler()
void MacOpenFile(const wxString &fileName) override
void OnMenuNew(wxCommandEvent &event)
static void OnThemeChange(struct ThemeChangeMessage)
bool SafeMRUOpen(const wxString &fileName)
void OnReceiveCommand(AppCommandEvent &event)
std::unique_ptr< IPCServ > mIPCServ
Definition: AudacityApp.h:126
void OnSocketEvent(wxSocketEvent &evt)
bool OnInit() override
Base class for exceptions specially processed by the application.
virtual void DelayedHandlerAction()=0
Action to do in the main thread at idle time of the event loop.
static std::unique_ptr< AudacityFileConfig > Create(const wxString &appName={}, const wxString &vendorName={}, const wxString &localFilename={}, const wxString &globalFilename={}, long style=wxCONFIG_USE_LOCAL_FILE|wxCONFIG_USE_GLOBAL_FILE, const wxMBConv &conv=wxConvAuto())
Require a call to this factory, to guarantee proper two-phase initialization.
static AudacityLogger * Get()
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
static void Init()
Definition: AudioIO.cpp:198
static void Deinit()
Definition: AudioIO.cpp:226
static AudioIO * Get()
Definition: AudioIO.cpp:133
static Clipboard & Get()
Definition: Clipboard.cpp:29
void Clear()
Definition: Clipboard.cpp:41
static CommandManager & Get(AudacityProject &project)
void RemoveDuplicateShortcuts()
virtual bool DeleteEntry(const wxString &key, bool bDeleteGroupIfEmpty=true) wxOVERRIDE
Definition: FileConfig.cpp:209
void GetVersionKeysInit(int &major, int &minor, int &micro) const
Definition: FileConfig.h:56
void SetVersionKeysInit(int major, int minor, int micro)
Definition: FileConfig.h:50
virtual bool GetNextGroup(wxString &str, long &lIndex) const wxOVERRIDE
Definition: FileConfig.cpp:108
virtual bool DeleteGroup(const wxString &key) wxOVERRIDE
Definition: FileConfig.cpp:219
virtual bool GetFirstGroup(wxString &str, long &lIndex) const wxOVERRIDE
Definition: FileConfig.cpp:103
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
virtual const wxString & GetPath() const wxOVERRIDE
Definition: FileConfig.cpp:98
virtual void SetPath(const wxString &strPath) wxOVERRIDE
Definition: FileConfig.cpp:93
Similar to wxFileHistory, but customized to our needs.
Definition: FileHistory.h:26
void Clear()
Definition: FileHistory.cpp:90
void Save(wxConfigBase &config)
static FileHistory & Global()
Definition: FileHistory.cpp:37
Abstract base class used in importing a file.
static void Destroy()
Destroys the dialog to prevent Audacity from hanging on exit.
bool OnExec(const wxString &WXUNUSED(topic), const wxString &data)
wxConnectionBase * OnAcceptConnection(const wxString &topic) override
IPCServ(const wxString &appl)
static Importer & Get()
Definition: Import.cpp:71
bool Initialize()
Definition: Import.cpp:132
bool Terminate()
Definition: Import.cpp:171
static void Destroy()
Destroys the log window (if any)
Definition: LogWindow.cpp:178
static void RebuildAllMenuBars()
Definition: Menus.cpp:687
static ModuleManager & Get()
int Dispatch(ModuleDispatchTypes type)
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
static bool IsHostProcess(int argc, wxChar **argv)
Returns true if current process is considered to be a plugin host process.
Definition: PluginHost.cpp:208
std::map< wxString, std::vector< wxString > > CheckPluginUpdates()
Ensures that all currently registered plugins still exist and scans for new ones.
void Initialize(FileConfigFactory factory)
static PluginManager & Get()
int ShowModal() override
std::vector< PrefsPanel::PrefsNode > Factories
Definition: PrefsPanel.h:69
int GetAudioIOToken() const
static ProjectAudioIO & Get(AudacityProject &project)
void Stop(bool stopStream=true)
static ProjectAudioManager & Get(AudacityProject &project)
static bool InitializeSQL()
static bool IsAlreadyOpen(const FilePath &projPathName)
static ProjectHistory & Get(AudacityProject &project)
static AudacityProject * New()
static void SaveWindowSize()
static void OpenFiles(AudacityProject *proj)
static AudacityProject * OpenProject(AudacityProject *pGivenProject, const FilePath &fileNameArg, bool addtohistory, bool reuseNonemptyProject)
Open a file into an AudacityProject, returning the project, or nullptr for failure.
static void SetClosingAll(bool closing)
static ProjectSettings & Get(AudacityProject &project)
bool GetShowSplashScreen() const
static ProjectWindow & Get(AudacityProject &project)
void RedrawProject(const bool bForceWaveTracks=false)
static Scrubber & Get(AudacityProject &project)
Definition: Scrubbing.cpp:187
static void SetMaxDiskBlockSize(size_t bytes)
Definition: Sequence.cpp:1700
static void DoHelpWelcome(AudacityProject &project)
static bool LoadPreferredTheme()
Definition: Theme.cpp:162
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:486
void ClearPendingTracks(ListOfTracks *pAdded=nullptr)
Definition: Track.cpp:1103
bool RegisterScheme(std::string_view scheme)
Associates a new scheme with Audacity.
static URLSchemesRegistry & Get()
Retrieves the registry instance.
void HandleURL(std::string_view url)
static void Start(bool suppressModal)
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
An implementation of BasicUI::Services in terms of the wxWidgets toolkit.
Services * Install(Services *pInstance)
Install an implementation; return the previously installed instance.
Definition: BasicUI.cpp:196
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:206
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:194
FILES_API FilePath Configuration()
FILES_API FilePath ResourcesDir()
FILES_API wxString PreferenceKey(FileNames::Operation op, FileNames::PathType type)
FILES_API FilePath StateDir()
Audacity user state directory.
FILES_API void InitializePathList()
FILES_API FilePath DataDir()
Audacity user data directory.
FILES_API void UpdateDefaultPath(Operation op, const FilePath &path)
FILES_API const FilePaths & AudacityPathList()
A list of directories that should be searched for Audacity files (plug-ins, help files,...
AUDACITY_DLL_API wxString SetLang(const wxString &lang)
Definition: GUISettings.cpp:19
bool Dispatch()
Definition: Journal.cpp:291
bool Begin(const FilePath &dataDir)
Definition: Journal.cpp:226
void SetInputFileName(const wxString &path)
Definition: Journal.cpp:221
int GetExitCode()
Definition: Journal.cpp:386
wxString GetSystemLanguageCode(const FilePaths &pathList)
Definition: Languages.cpp:83
wxString GetLocaleName()
Definition: Languages.cpp:390
void DestroyRegistry()
Definition: Menus.cpp:267
FILES_API bool IsTempDirectoryNameOK(const FilePath &Name)
FILES_API wxString TempDir()
FILES_API void ResetTempDir()
FILES_API const FilePath & DefaultTempDir()
THEME_RESOURCES_API void Load()
BuiltinCommandsModule::Registration< CompareAudioCommand > reg
std::string ToUTF8(const std::wstring &wstr)
Implementation of BasicUI using wxWidgets.