613 lines
17 KiB
C++
613 lines
17 KiB
C++
#include "../Audacity.h"
|
|
#include "../Experimental.h"
|
|
|
|
#include <wx/bmpbuttn.h>
|
|
#include <wx/textctrl.h>
|
|
#include <wx/frame.h>
|
|
|
|
#include "../AboutDialog.h"
|
|
#include "../AllThemeResources.h"
|
|
#include "../AudacityLogger.h"
|
|
#include "../AudioIOBase.h"
|
|
#include "../CommonCommandFlags.h"
|
|
#include "../CrashReport.h"
|
|
#include "../Dependencies.h"
|
|
#include "../FileNames.h"
|
|
#include "../HelpText.h"
|
|
#include "../Menus.h"
|
|
#include "../Prefs.h"
|
|
#include "../Project.h"
|
|
#include "../ProjectSelectionManager.h"
|
|
#include "../ShuttleGui.h"
|
|
#include "../SplashDialog.h"
|
|
#include "../Theme.h"
|
|
#include "../commands/CommandContext.h"
|
|
#include "../commands/CommandManager.h"
|
|
#include "../prefs/PrefsDialog.h"
|
|
#include "../widgets/AudacityMessageBox.h"
|
|
#include "../widgets/HelpSystem.h"
|
|
|
|
#if defined(EXPERIMENTAL_CRASH_REPORT)
|
|
#include <wx/debugrpt.h>
|
|
#endif
|
|
|
|
// private helper classes and functions
|
|
namespace {
|
|
|
|
void ShowDiagnostics(
|
|
AudacityProject &project, const wxString &info,
|
|
const TranslatableString &description, const wxString &defaultPath,
|
|
bool fixedWidth = false)
|
|
{
|
|
auto &window = GetProjectFrame( project );
|
|
wxDialogWrapper dlg( &window, wxID_ANY, description);
|
|
dlg.SetName();
|
|
ShuttleGui S(&dlg, eIsCreating);
|
|
|
|
wxTextCtrl *text;
|
|
S.StartVerticalLay();
|
|
{
|
|
text = S.Id(wxID_STATIC)
|
|
.Style(wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH)
|
|
.AddTextWindow("");
|
|
|
|
S.AddStandardButtons(eOkButton | eCancelButton);
|
|
}
|
|
S.EndVerticalLay();
|
|
|
|
if (fixedWidth) {
|
|
auto style = text->GetDefaultStyle();
|
|
style.SetFontFamily( wxFONTFAMILY_TELETYPE );
|
|
text->SetDefaultStyle(style);
|
|
}
|
|
|
|
*text << info;
|
|
|
|
dlg.FindWindowById(wxID_OK)->SetLabel(_("&Save"));
|
|
dlg.SetSize(350, 450);
|
|
|
|
if (dlg.ShowModal() == wxID_OK)
|
|
{
|
|
const auto fileDialogTitle = XO("Save %s").Format( description );
|
|
wxString fName = FileNames::SelectFile(FileNames::Operation::Export,
|
|
fileDialogTitle,
|
|
wxEmptyString,
|
|
defaultPath,
|
|
wxT("txt"),
|
|
{ FileNames::TextFiles },
|
|
wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
|
|
&window);
|
|
if (!fName.empty())
|
|
{
|
|
if (!text->SaveFile(fName))
|
|
{
|
|
AudacityMessageBox(
|
|
XO("Unable to save %s").Format( description ),
|
|
fileDialogTitle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @brief Class which makes a dialog for displaying quick fixes to common issues.
|
|
*
|
|
* This class originated with the 'Stuck in a mode' problem, where far too many
|
|
* users get into a mode without realising, and don't know how to get out.
|
|
* It is a band-aid, and we should do more towards a full and proper solution
|
|
* where there are fewer special modes, and they don't persisit.
|
|
*/
|
|
class QuickFixDialog : public wxDialogWrapper
|
|
{
|
|
public:
|
|
QuickFixDialog(wxWindow * pParent, AudacityProject &project);
|
|
void Populate();
|
|
void PopulateOrExchange(ShuttleGui & S);
|
|
void AddStuck( ShuttleGui & S, bool & bBool, wxString Pref,
|
|
const TranslatableString &Prompt, wxString Help );
|
|
|
|
void OnOk(wxCommandEvent &event);
|
|
void OnCancel(wxCommandEvent &event);
|
|
void OnHelp(wxCommandEvent &event);
|
|
void OnFix(wxCommandEvent &event);
|
|
|
|
wxString StringFromEvent( wxCommandEvent &event );
|
|
|
|
AudacityProject &mProject;
|
|
|
|
int mItem;
|
|
bool mbSyncLocked;
|
|
bool mbInSnapTo;
|
|
bool mbSoundActivated;
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
#define FixButtonID 7001
|
|
#define HelpButtonID 7011
|
|
#define FakeButtonID 7021
|
|
|
|
BEGIN_EVENT_TABLE(QuickFixDialog, wxDialogWrapper)
|
|
EVT_BUTTON(wxID_OK, QuickFixDialog::OnOk)
|
|
EVT_BUTTON(wxID_CANCEL, QuickFixDialog::OnCancel)
|
|
EVT_BUTTON(wxID_HELP, QuickFixDialog::OnHelp)
|
|
EVT_COMMAND_RANGE(FixButtonID, HelpButtonID-1, wxEVT_BUTTON, QuickFixDialog::OnFix)
|
|
EVT_COMMAND_RANGE(HelpButtonID, FakeButtonID-1, wxEVT_BUTTON, QuickFixDialog::OnHelp)
|
|
END_EVENT_TABLE();
|
|
|
|
QuickFixDialog::QuickFixDialog(wxWindow * pParent, AudacityProject &project) :
|
|
wxDialogWrapper(pParent, wxID_ANY, XO("Do you have these problems?"),
|
|
wxDefaultPosition, wxDefaultSize,
|
|
wxDEFAULT_DIALOG_STYLE )
|
|
, mProject{ project }
|
|
{
|
|
const long SNAP_OFF = 0;
|
|
|
|
gPrefs->Read(wxT("/GUI/SyncLockTracks"), &mbSyncLocked, false);
|
|
mbInSnapTo = gPrefs->Read(wxT("/SnapTo"), SNAP_OFF) !=0;
|
|
gPrefs->Read(wxT("/AudioIO/SoundActivatedRecord"), &mbSoundActivated, false);
|
|
|
|
ShuttleGui S(this, eIsCreating);
|
|
PopulateOrExchange(S);
|
|
|
|
Fit();
|
|
auto sz = GetSize();
|
|
SetMinSize( sz );
|
|
SetMaxSize( sz );
|
|
|
|
// The close button has the cancel id and acts exactly the same as cancel.
|
|
wxButton * pWin = (wxButton*)FindWindowById( wxID_CANCEL );
|
|
if( pWin )
|
|
pWin->SetFocus( );
|
|
Center();
|
|
}
|
|
|
|
void QuickFixDialog::AddStuck(
|
|
ShuttleGui & S, bool & bBool, wxString Pref,
|
|
const TranslatableString &Prompt, wxString Help )
|
|
{
|
|
mItem++;
|
|
if( !bBool)
|
|
return;
|
|
S.AddFixedText( Prompt );
|
|
S.Id(FixButtonID + mItem).AddButton( XO("Fix") )->SetClientObject(
|
|
safenew wxStringClientData(Pref));
|
|
|
|
{
|
|
// Replace standard Help button with smaller icon button.
|
|
// bs->AddButton(safenew wxButton(parent, wxID_HELP));
|
|
auto b = safenew wxBitmapButton(S.GetParent(), HelpButtonID+mItem, theTheme.Bitmap( bmpHelpIcon ));
|
|
b->SetToolTip( _("Help") );
|
|
b->SetLabel(_("Help")); // for screen readers
|
|
b->SetClientObject( safenew wxStringClientData( Help ));
|
|
S.AddWindow( b );
|
|
}
|
|
}
|
|
|
|
void QuickFixDialog::PopulateOrExchange(ShuttleGui & S)
|
|
{
|
|
|
|
S.StartVerticalLay(1);
|
|
S.StartStatic( XO("Quick Fixes"));
|
|
|
|
// These aren't all possible modes one can be stuck in, but they are some of them.
|
|
bool bStuckInMode = mbSyncLocked || mbInSnapTo || mbSoundActivated;
|
|
|
|
if( !bStuckInMode ){
|
|
SetLabel(XO("Nothing to do"));
|
|
S.AddFixedText(XO("No quick, easily fixed problems were found"));
|
|
}
|
|
else {
|
|
S.StartMultiColumn(3, wxALIGN_CENTER);
|
|
{
|
|
mItem = -1;
|
|
|
|
// Use # in the URLs to ensure we go to the online version of help.
|
|
// Local help may well not be installed.
|
|
AddStuck( S, mbSyncLocked, "/GUI/SyncLockTracks",
|
|
XO("Clocks on the Tracks"), "Quick_Fix#sync_lock" );
|
|
AddStuck( S, mbInSnapTo, "/SnapTo",
|
|
XO("Can't select precisely"), "Quick_Fix#snap_to" );
|
|
AddStuck( S, mbSoundActivated, "/AudioIO/SoundActivatedRecord",
|
|
XO("Recording stops and starts"),
|
|
"Quick_Fix#sound_activated_recording" );
|
|
}
|
|
S.EndMultiColumn();
|
|
}
|
|
S.EndStatic();
|
|
|
|
S.StartHorizontalLay(wxALIGN_CENTER_HORIZONTAL, 0);
|
|
S.AddStandardButtons(eCloseButton + (bStuckInMode ? 0 : eHelpButton));
|
|
S.EndHorizontalLay();
|
|
|
|
S.EndVerticalLay();
|
|
|
|
wxButton * pBtn = (wxButton*)FindWindowById( wxID_HELP );
|
|
if( pBtn )
|
|
pBtn->SetClientObject( safenew wxStringClientData( "Quick_Fix#" ));
|
|
|
|
}
|
|
|
|
void QuickFixDialog::OnOk(wxCommandEvent &event)
|
|
{
|
|
(void)event;// Compiler food
|
|
EndModal(wxID_OK);
|
|
}
|
|
|
|
void QuickFixDialog::OnCancel(wxCommandEvent &event)
|
|
{
|
|
(void)event;// Compiler food
|
|
EndModal(wxID_CANCEL);
|
|
}
|
|
|
|
wxString QuickFixDialog::StringFromEvent( wxCommandEvent &event )
|
|
{
|
|
wxButton * pBtn = (wxButton*)event.GetEventObject();
|
|
if( !pBtn ){
|
|
wxFAIL_MSG( "Event Object not found");
|
|
return "";
|
|
}
|
|
wxStringClientData * pStrCd = (wxStringClientData*)(pBtn->GetClientObject());
|
|
if( !pStrCd ){
|
|
wxFAIL_MSG( "Client Data not found");
|
|
return "";
|
|
}
|
|
wxString Str = pStrCd->GetData();
|
|
if( Str.empty()){
|
|
wxFAIL_MSG( "String data empty");
|
|
return "";
|
|
}
|
|
return Str;
|
|
}
|
|
|
|
void QuickFixDialog::OnHelp(wxCommandEvent &event)
|
|
{
|
|
HelpSystem::ShowHelp(this, StringFromEvent( event ), true);
|
|
}
|
|
|
|
void QuickFixDialog::OnFix(wxCommandEvent &event)
|
|
{
|
|
wxString Str = StringFromEvent( event );
|
|
gPrefs->Write( Str, 0);
|
|
gPrefs->Flush();
|
|
|
|
{
|
|
// Sadly SnapTo has to be handled specially, as it is not part of the standard
|
|
// preference dialogs.
|
|
if( Str == "/SnapTo" )
|
|
{
|
|
ProjectSelectionManager::Get( mProject ).AS_SetSnapTo( 0 );
|
|
}
|
|
else
|
|
{
|
|
// This is overkill (aka slow), as all preferences are reloaded and all
|
|
// toolbars recreated.
|
|
// Overkill probably doesn't matter, as this command is infrequently used.
|
|
DoReloadPreferences( mProject );
|
|
}
|
|
}
|
|
|
|
// Change the label after doing the fix, as the fix may take a second or two.
|
|
wxButton * pBtn = (wxButton*)event.GetEventObject();
|
|
if( pBtn )
|
|
pBtn->SetLabel( _("Fixed") );
|
|
|
|
// The close button has the cancel id and acts exactly the same as cancel.
|
|
wxButton * pWin = (wxButton*)FindWindowById( wxID_CANCEL );
|
|
if( pWin )
|
|
pWin->SetFocus( );
|
|
}
|
|
|
|
}
|
|
|
|
namespace HelpActions {
|
|
|
|
// exported helper functions
|
|
|
|
// Menu handler functions
|
|
|
|
struct Handler : CommandHandlerObject {
|
|
|
|
void OnQuickFix(const CommandContext &context)
|
|
{
|
|
auto &project = context.project;
|
|
QuickFixDialog dlg( &GetProjectFrame( project ), project );
|
|
dlg.ShowModal();
|
|
}
|
|
|
|
void OnQuickHelp(const CommandContext &context)
|
|
{
|
|
auto &project = context.project;
|
|
HelpSystem::ShowHelp(
|
|
&GetProjectFrame( project ),
|
|
wxT("Quick_Help"));
|
|
}
|
|
|
|
void OnManual(const CommandContext &context)
|
|
{
|
|
auto &project = context.project;
|
|
HelpSystem::ShowHelp(
|
|
&GetProjectFrame( project ),
|
|
wxT("Main_Page"));
|
|
}
|
|
|
|
void OnAudioDeviceInfo(const CommandContext &context)
|
|
{
|
|
auto &project = context.project;
|
|
auto gAudioIO = AudioIOBase::Get();
|
|
wxString info = gAudioIO->GetDeviceInfo();
|
|
ShowDiagnostics( project, info,
|
|
XO("Audio Device Info"), wxT("deviceinfo.txt") );
|
|
}
|
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
void OnMidiDeviceInfo(const CommandContext &context)
|
|
{
|
|
auto &project = context.project;
|
|
auto gAudioIO = AudioIOBase::Get();
|
|
wxString info = gAudioIO->GetMidiDeviceInfo();
|
|
ShowDiagnostics( project, info,
|
|
XO("MIDI Device Info"), wxT("midideviceinfo.txt") );
|
|
}
|
|
#endif
|
|
|
|
void OnShowLog( const CommandContext &context )
|
|
{
|
|
auto logger = AudacityLogger::Get();
|
|
if (logger) {
|
|
logger->Show();
|
|
}
|
|
}
|
|
|
|
#if defined(EXPERIMENTAL_CRASH_REPORT)
|
|
void OnCrashReport(const CommandContext &WXUNUSED(context) )
|
|
{
|
|
// Change to "1" to test a real crash
|
|
#if 0
|
|
char *p = 0;
|
|
*p = 1234;
|
|
#endif
|
|
CrashReport::Generate(wxDebugReport::Context_Current);
|
|
}
|
|
#endif
|
|
|
|
void OnCheckDependencies(const CommandContext &context)
|
|
{
|
|
auto &project = context.project;
|
|
::ShowDependencyDialogIfNeeded(&project, false);
|
|
}
|
|
|
|
void OnMenuTree(const CommandContext &context)
|
|
{
|
|
auto &project = context.project;
|
|
|
|
using namespace MenuTable;
|
|
struct MyVisitor : MenuVisitor
|
|
{
|
|
using MenuVisitor::MenuVisitor;
|
|
|
|
enum : unsigned { TAB = 3 };
|
|
void BeginGroup( GroupItem &item, const Path& ) override
|
|
{
|
|
auto pItem = &item;
|
|
if ( pItem->Transparent() ) {
|
|
}
|
|
else if ( const auto pGroup = dynamic_cast<MenuSection*>( pItem ) ) {
|
|
if ( !needSeparator.empty() )
|
|
needSeparator.back() = true;
|
|
}
|
|
else {
|
|
MaybeEmitSeparator();
|
|
Indent();
|
|
// using GET for alpha only diagnostic tool
|
|
info += item.name.GET();
|
|
Return();
|
|
indentation = wxString{ ' ', TAB * ++level };
|
|
needSeparator.push_back( false );
|
|
firstItem.push_back( true );
|
|
}
|
|
}
|
|
|
|
void EndGroup( GroupItem &item, const Path& ) override
|
|
{
|
|
auto pItem = &item;
|
|
if ( pItem->Transparent() ) {
|
|
}
|
|
else if ( const auto pGroup = dynamic_cast<MenuSection*>( pItem ) ) {
|
|
if ( !needSeparator.empty() )
|
|
needSeparator.back() = true;
|
|
}
|
|
else {
|
|
firstItem.pop_back();
|
|
needSeparator.pop_back();
|
|
indentation = wxString{ ' ', TAB * --level };
|
|
}
|
|
}
|
|
|
|
void Visit( SingleItem &item, const Path& ) override
|
|
{
|
|
MaybeEmitSeparator();
|
|
|
|
// using GET for alpha only diagnostic tool
|
|
Indent();
|
|
info += item.name.GET();
|
|
Return();
|
|
}
|
|
|
|
void MaybeEmitSeparator()
|
|
{
|
|
static const wxString separatorName{ '=', 20 };
|
|
|
|
bool separate = false;
|
|
if ( !needSeparator.empty() ) {
|
|
separate = needSeparator.back() && !firstItem.back();
|
|
needSeparator.back() = false;
|
|
firstItem.back() = false;
|
|
}
|
|
|
|
if ( separate ) {
|
|
Indent();
|
|
info += separatorName;
|
|
Return();
|
|
}
|
|
}
|
|
|
|
void Indent() { info += indentation; }
|
|
void Return() { info += '\n'; }
|
|
|
|
unsigned level{};
|
|
wxString indentation;
|
|
wxString info;
|
|
|
|
std::vector<bool> firstItem;
|
|
std::vector<bool> needSeparator;
|
|
} visitor{ project };
|
|
|
|
MenuManager::Visit( visitor );
|
|
|
|
ShowDiagnostics( project, visitor.info,
|
|
XO("Menu Tree"), wxT("menutree.txt"), true );
|
|
}
|
|
|
|
void OnCheckForUpdates(const CommandContext &WXUNUSED(context))
|
|
{
|
|
::OpenInDefaultBrowser( VerCheckUrl());
|
|
}
|
|
|
|
void OnAbout(const CommandContext &context)
|
|
{
|
|
#ifdef __WXMAC__
|
|
// Modeless dialog, consistent with other Mac applications
|
|
// Simulate the application Exit menu item
|
|
wxCommandEvent evt{ wxEVT_MENU, wxID_ABOUT };
|
|
wxTheApp->AddPendingEvent( evt );
|
|
#else
|
|
auto &project = context.project;
|
|
auto &window = GetProjectFrame( project );
|
|
|
|
// Windows and Linux still modal.
|
|
AboutDialog dlog( &window );
|
|
dlog.ShowModal();
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
// Legacy handlers, not used as of version 2.3.0
|
|
|
|
// Only does the update checks if it's an ALPHA build and not disabled by
|
|
// preferences.
|
|
void MayCheckForUpdates(AudacityProject &project)
|
|
{
|
|
#ifdef IS_ALPHA
|
|
OnCheckForUpdates(project);
|
|
#endif
|
|
}
|
|
|
|
void OnHelpWelcome(const CommandContext &context)
|
|
{
|
|
SplashDialog::DoHelpWelcome( context.project );
|
|
}
|
|
|
|
#endif
|
|
|
|
}; // struct Handler
|
|
|
|
} // namespace
|
|
|
|
static CommandHandlerObject &findCommandHandler(AudacityProject &) {
|
|
// Handler is not stateful. Doesn't need a factory registered with
|
|
// AudacityProject.
|
|
static HelpActions::Handler instance;
|
|
return instance;
|
|
};
|
|
|
|
// Menu definitions
|
|
|
|
#define FN(X) (& HelpActions::Handler :: X)
|
|
|
|
namespace {
|
|
using namespace MenuTable;
|
|
BaseItemSharedPtr HelpMenu()
|
|
{
|
|
static BaseItemSharedPtr menu{
|
|
( FinderScope{ findCommandHandler },
|
|
Menu( wxT("Help"), XO("&Help"),
|
|
Section( "Basic",
|
|
// QuickFix menu item not in Audacity 2.3.1 whilst we discuss further.
|
|
#ifdef EXPERIMENTAL_DA
|
|
// DA: Has QuickFix menu item.
|
|
Command( wxT("QuickFix"), XXO("&Quick Fix..."), FN(OnQuickFix),
|
|
AlwaysEnabledFlag ),
|
|
// DA: 'Getting Started' rather than 'Quick Help'.
|
|
Command( wxT("QuickHelp"), XXO("&Getting Started"), FN(OnQuickHelp) ),
|
|
// DA: Emphasise it is the Audacity Manual (No separate DA manual).
|
|
Command( wxT("Manual"), XXO("Audacity &Manual"), FN(OnManual) )
|
|
#else
|
|
Command( wxT("QuickHelp"), XXO("&Quick Help..."), FN(OnQuickHelp),
|
|
AlwaysEnabledFlag ),
|
|
Command( wxT("Manual"), XXO("&Manual..."), FN(OnManual),
|
|
AlwaysEnabledFlag )
|
|
#endif
|
|
),
|
|
|
|
#ifdef __WXMAC__
|
|
Items
|
|
#else
|
|
Section
|
|
#endif
|
|
( "Other",
|
|
Menu( wxT("Diagnostics"), XO("&Diagnostics"),
|
|
Command( wxT("DeviceInfo"), XXO("Au&dio Device Info..."),
|
|
FN(OnAudioDeviceInfo),
|
|
AudioIONotBusyFlag() ),
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
Command( wxT("MidiDeviceInfo"), XXO("&MIDI Device Info..."),
|
|
FN(OnMidiDeviceInfo),
|
|
AudioIONotBusyFlag() ),
|
|
#endif
|
|
Command( wxT("Log"), XXO("Show &Log..."), FN(OnShowLog),
|
|
AlwaysEnabledFlag ),
|
|
#if defined(EXPERIMENTAL_CRASH_REPORT)
|
|
Command( wxT("CrashReport"), XXO("&Generate Support Data..."),
|
|
FN(OnCrashReport), AlwaysEnabledFlag ),
|
|
#endif
|
|
Command( wxT("CheckDeps"), XXO("Chec&k Dependencies..."),
|
|
FN(OnCheckDependencies),
|
|
AudioIONotBusyFlag() )
|
|
|
|
#ifdef IS_ALPHA
|
|
,
|
|
// Menu explorer. Perhaps this should become a macro command
|
|
Command( wxT("MenuTree"), XXO("Menu Tree..."),
|
|
FN(OnMenuTree),
|
|
AlwaysEnabledFlag )
|
|
#endif
|
|
)
|
|
#ifndef __WXMAC__
|
|
),
|
|
|
|
Section( "",
|
|
#else
|
|
,
|
|
#endif
|
|
|
|
// DA: Does not fully support update checking.
|
|
#ifndef EXPERIMENTAL_DA
|
|
Command( wxT("Updates"), XXO("&Check for Updates..."),
|
|
FN(OnCheckForUpdates),
|
|
AlwaysEnabledFlag ),
|
|
#endif
|
|
Command( wxT("About"), XXO("&About Audacity..."), FN(OnAbout),
|
|
AlwaysEnabledFlag )
|
|
)
|
|
) ) };
|
|
return menu;
|
|
}
|
|
|
|
AttachedItem sAttachment1{
|
|
wxT(""),
|
|
Shared( HelpMenu() )
|
|
};
|
|
|
|
}
|
|
|
|
#undef FN
|